目录:
一、ECMA Script部分
数据类型                                       								110
布尔值、逻辑运算符																173
for循环、break、continue														202
随机生成小学生100以内加减乘除法、Math.random() 									222
typeof()																	261
类型转换																		301
valueOf()与toString()														484
函数   						            									716
(函数的定义方式、函数的实参与形参、arguments) 										
立即执行函数 																	925
预编译       																993
作用域链、执行期上下文															1265
闭包                            												1358
对象、对象的属性  				 												1668
包装类(基本包装类性)															1789
construct构造器			           											1954
构造函数内部原理																1989
new关键字、构造函数三段论	 													2036
原型、原型链																	2152
prototype和__proto__的区别													2245
给原型对象赋值时Person.prototype.name、person.__proto__.name、					2413
Person.prototype、person.__proto__与new标志的位置关系对对象原型属性的影响   		
JavaScript对象、constructor、__proto__之深入剖析 								2540
call/apply                   												2625
继承                															2687
圣杯模式继承 																	2737
组合函数继承 																	2781
方法的连续调用、属性拼接 														2828
for in循环、hasOwnProperty													2868
instanceof 																	2987				
为什么用Object.prototype.toString.call(obj)检测对象类型						3068
this                														3215
三目运算符? :  																3624
克隆             															3642
数组 																		3709
类数组 																		4038
私有化变量																	4116
with(){} 																	4161
try...catch 																4346
es5严格/标准模式																4378
return与分号插入机制															4403
eval()与new Function() 														4421
二、dom部分
Emmet插件使用方法																4514
Dom选择器、节点类型 															4682
DOM里各节点方法的继承树(图) 													4755
几个练习题 																	4775
DOM基本操作(节点的增删改查) 													4864
Element节点的属性、方法 														4913
date对象、定时器 																4997
BOM基本操作 																	5093
脚本化css 																	5211
事件处理程序的绑定与解除 														5252
事件处理程序的运行环境(this指向) 												5321
事件处理模型(事件冒泡、捕获)及取消  											5387
事件对象、事件源对象与事件委托 													5512
事件分类 																	5578
json																		5684
浏览器的渲染引擎																5705
异步加载js																	5718
js加载时间线（浏览器加载时间线）												5809
正则表达式 																	5880
ES5数组扩展方法应用															6759


































外部引入：<script type="text/javascript" src="xxx.js"></script>

变量名命名规则：
1.变量名必须以英文字母、_、$开头
2.变量名可以包括英文字母、_、$、数字
3.不可以用系统的关键字、保留字作为变量名

*********************************
*                               *
*			 数据类型           	*
*                               *
*********************************
数据分为两种类型(数据类型)：

1.不可改变的原始值（栈数据）,数据名里存放的是实际数据,原始值只有5种：
【1】Number  	:123,0,-6.37
【2】String 	: "" , ''
【3】boolean 	: True / False
【4】undefined	:undefined
【5】null		:占位用
<script type="text/javascript">
	var a = 10;           //此时a里存放的数据是10
	var b = a;            //此时把a里的数据拷贝一份给b
		a = 20;           //此时a变为了20，b仍然等于10
</script>

2.引用值（堆数据）,除原始值以外的都叫引用值,数据名引用的都是数据地址,引用值包括：
引用值指的是复合数据类型的值，包括：Object、Function、Array、Date、RegExp;引用值是把引用变量的名称储存在栈中,但是把其实际对象储存在堆中，
且存在一个指针,这个指针由变量名指向储存在堆中的实际对象。
当把引用对象传递给另一个变量时，复制的其实是指向实际对象的指针，此时两者指向的是同一个数据，
若通过方法改变其中一个变量的值，则访问另一个变量时，其值也会随之加以改变;
但若不是通过方法而是通过重新赋值此时相当于重新开了一个房间该值的原指针改变,则另外一个值不会随他的改变而改变。

<script type="text/javascript">

	var arr = [1,2];      //此时arr引用的是数组[1，2]所在的地址
	var arr1 = arr;       //此时arr1与arr同时引用[1,2]
	arr.push(3);          //这里是对栈进行的操作，此时数组里的数变为[1,2,3]，arr与arr1的值都为[1,2,3]

	var arr = [1,2];      //此时arr引用的是数组[1，2]所在的地址
	var arr1 = arr;       //此时arr1与arr同时引用[1,2]
	arr = [1,3];          //此时arr重新赋值给了[1,3],arr1仍然是[1,2]

</script>

array(数组)：
var arr = [,,,,,];
数组长度：arr.length;

object(对象)：
<script type="text/javascript">

	var object = {
		lastname : "yichaun",
		firstname: "li",
		age      : 20,
				 :undefined,
			 	 :false
	}

</script>

原始值和引用值的区别:
【1】存储位置不同，原始值在栈内存储，引用值在堆内存储
【2】原始值没有属性和方法，引用值才有

赋值的顺序，自右向左
计算的顺序，自左向右

a，b互换方法：
a = a + b;
b = a - b;
a = a - b;

**********************************
*                                *
*         布尔值、逻辑运算符		 *
*                                *
**********************************
一、Boolean(布尔值):
【1】false(6种):
0(-0), NaN, "", false, underfined, null
【2】true:
除去false其他全为true
二、逻辑运算符(&&  ||  ！)
【1】逻辑与(&&)
先看第一个表达式转换成布尔值的结果,如果结果为真,那么他会看第二个表达式转换为布尔值的结果,然后如果只有两个表达式的话,只要看到第二个表达式,就可以返回该表达式的值了
当第一个表达式转化为布尔值为false时直接返回第一个表达式的值（注意不是返回false）,相当于遇到布尔值为假就返回表达式的值(注意不是返回false)
逻辑与运算符当做短路语句：
	2 > 1 && document.write("abc")	//可以当if语句看：如果2 > 1，就打印"abc"
短路语句应用(防止data为空)：
	var data = ...;
	data && fun(data),因为执行fun(data)语句时，会用到data数据，此语句可以避免在data为空时调用fun函数
【2】逻辑或(||)
相当于遇到布尔值为真就返回表达式的值
【3】逻辑非
逻辑运算符举例:
<script type="text/javascript">
	console.log('0 || 1 = '+(0 || 1)); 	//打印 	0 || 1 = 1
	console.log('1 || 2 = '+(1 || 2)); 	//打印 	1 || 2 = 1
	console.log('0 && 1 = '+(0 && 1)); 	//打印 	0 && 1 = 0
	console.log('1 && 2 = '+(1 && 2)); 	//打印 	1 && 2 = 2
</script>
**********************************
*                                *
*      for循环、break、continue 	 *
*                                *
**********************************
【1】巧用for循环：
<script type="text/javascript">
	var i = 1;
	var count = 0;
	for(;i;){					//相当于while(i){...}
		document.write('a');
		count ++;
		if(count == 10)
		{
			i = 0;
		}
	}
</script>
【2】break:跳出循环;
【3】continue：终止本次循环（本次循环continue以后的语句不再执行）,进行下一次循环。
*************************************************
*											 	*
*  随机生成小学生100以内加减乘除法、Math.random() 	*
*												*
*************************************************
Math.random() -> [0,1)
<script type="text/javascript">
	for(i = 0; i <100; i++){										//出100道题
	var c = Math.floor((Math.floor(Math.random()*100)+1)/25)+1,		//随机产生1,2,3,4,随机选择运算符号
		x = Math.floor(Math.random()*89)+10,						//随机产生10-99数字整数
		y = Math.floor(Math.random()*89)+10;						//随机产生10-99数字整数
		switch(c){
		case 1:														//产生加法运算
			document.write(x + '+' + y + '=' + '<br>');							
			break;
		case 2: 													//产生乘法运算
			document.write(x + '×' + y + '=' + '<br>');							
			break;
		case 3: 													//产生减法运算
			if(x > y){
				document.write(x + '-' + y + '=' + '<br>');
			}else{
				document.write(y + '-' + x + '=' + '<br>');
			};															
			break;
		case 4: 													//产生除法运算
			if(x > y){
				while(x % y){
					y++;
				}
				document.write(x + '÷' + y + '=' + '<br>');
			}else{
				while(y % x){
					x--;
				}
				document.write(y + '÷' + x + '=' + '<br>');
			};
		};
	};
</script>
*********************************
*								*
*           typeof()            *
*								*
*********************************
typeof()的返回值有6种:
【1】number
【2】string
【3】boolean
【4】object(数组与null都属于object)
【5】function
【6】undefined

特殊值的typeof()举例：
	typeof(undefined)		undefined, 
	typeof(null)			object, 
	typeof(NaN)				number, 
	typeof("")				string, 
	typeof(0)				number, 
	typeof(false)			boolean;
	
注意:若a未声明，直接typeof(a),不报错，返回的是字符串类型的"undefined"
问题：
【1】在Javascript中，typeof(undefined) == undefined成立吗？
答案：不成立，全局函数 typeof()返回值类型为字符串类型，六个可能值： "number"， "string"， "boolean"， "object" ，"function"， 和 "undefined"。
只有typeof(undefined) =="undefined",才是成立的。
【2】typeof(null) == 'undefined'成立吗？
答案：不成立，null是Object类型的数据，故typeof(null) == 'object'成立。
【3】typeof([1,2]) == 'array'成立吗？
答案：不成立，typeof()返回值没有"array" 类型，同样也没有"date"类型，"array"属于"object"类型。
【4】undefined == undefined成立吗？
答案：成立。
【5】null == null成立吗？
答案：成立。
【6】NaN == NaN成立吗？
答案：不成立,NaN 不与任何值相等，包括其本身。要检测值是否为 NaN，请使用 isNaN 函数。
【7】typeof(NaN) == 'number'成立吗？
答案：成立,NaN是属于Number类型的数据。

终上所述，null是Object类型的数据，NaN是Number类型的数据，undefined则是undefined类型的数据，即没有定义，找不到该对象或对象的属性和方法。

*****************************
*                           *
*          类型转换        	*
*                          	*
*****************************
一.显示类型转换：
以下所有函数注意大小写

【1】 Number（mix）：
※※把变量的类型转化为数字类型,其中undefined和非数字符/字符串会转变为NaN
例：
<script type="text/javascript">
	var num = Number('123');
	console.log(typeof(num) + ":" + num);		//打印 number ：123 
</script>
【1.1】向上取整：
	Math.ceil(123.234)    //输出124
【1.2】向下取整：
	Math.floor(123.999)   //输出123
javascript可正常计算的范围 小数点前16位，后16位

【2】 parseInt(string,radix)：
把带有数字的变量转化为整形,从数字位开始,到第一个非数字位截断,例输入"123.6abc",则输出123,输入"a123"返回NaN,通常使用该函数从一个字符串中获取数值。
该函数的第二个参数是一个进制参数,通常可以忽略该参数,因为当解析的字符串是0开始的话,在ES3.0中他会被当做一个八进制数,而在ES5中发生了改变
例:
<script type="text/javascript">
	var month = '06xzc',
		year  = '09vcd';
	month = parseInt(month, 10);		//控制台输入month 输出6
	year  = parseInt(year , 10);		//控制台输入year 输出9
</script>
例：
<script type="text/javascript">
	var demo = "10";
	var num = parseInt(demo,16);   				//这里的16代表输入的“10”是16进制的数，取值范围是2-32，也叫radix参数
	console.log(typeof(num) + ":" + num);		//打印： number ：16   (这里打印的是十进制的数)
</script>
请问以下表达式的结果是什么？
<script type="text/javascript">
	parseInt(3,8);	//3
	parseInt(3,2);	//NaN
	parseInt(3,0);	//NaN
</script>
【3】 parseFloat(string):
把带有数字的变量转化为浮点形，从数字位开始，到第一个除点一位的非数字位截断，例输入"123.6.7abc", 则输出123.6
【4】 String(mix):
把变量的所有类型转化为字符串，输入123，输出“123”
【5】 toString(radix):
与String(mix)类似，但使用方法是："".toString
※※undefined和null不能使用toString()方法
例：
<script type="text/javascript">
	var demo = 123;
	var num = demo.toString();
	console.log(typeof(num) + ":" +num);		//打印 string：123

	var demo = 10;
	var num = demo.toString(8);    				//这里的8是把10转换为8进制，如果输入的是字符串则这个参数没有用
	console.log(typeof(num) + ":" +num)			//打印 string：12
</script>
把二进制1000转换为16进制的数：
<script type="text/javascript">
	var num = 1000;
	var test = parseInt（num, 2）;
	console.log(test.toString(16));
</script>
【6】 Boolean():
把变量的所有类型转化为布尔型
【6.1】fals(6种)： 0, NaN, "", false, underfined, null
【6.2】true：其余所有
<script type="text/javascript">
	console.log(Boolean(true - true));						//false
	console.log(Boolean(2 - true));							//true
	console.log(Boolean(1 - true));							//false
	console.log(true + 1);									//2	,可知把true当成数字计算时是把true转化成了1
</script>
二、隐式类型转换
【1】 isNaN() -->Number();
判断一个数是不是NaN
判断前先把里面的参数用Number()进行转化，再和NaN进行判断
例：
	console.log(isNaN("abc"));
打印 true
原因：Number('abc') --> NaN -->再和NaN进行比较
【2】 ++/--  +/-(单目正负符) -->Number();
例：
<script type="text/javascript">
	var a = +"abc";
	console.log(a + " : " + typeof(a));		//打印 NaN ： number ,注意NaN的数据类型是number
</script>
【3】 + (二目计算符）
两边只要有一个是字符串就调用字符串
【4】 -*/%  -->Number();
例：
<script type="text/javascript">
	var num = "2" * "1";
	console.log(typeof(num) + ":" + num);	//打印 number ： 2   ,因为出现了*,把两边的变量自动转换成number类型了。
</script>
【5】 && || !
1)逻辑与 &&：
先看第一个表达式转换成布尔值的结果，如果结果为真，那么他会看第二个表达式转换为布尔值的结果，然后如果只有两个表达式的话，只要看到第二个表达式，就可以返回该表达式的值了。
当第一个表达式转化为布尔值为false时直接返回第一个表达式的值（注意不是返回false）,相当于遇到布尔值为假就返回表达式的值。
2)逻辑或 ||：
相当于遇到布尔值为真就返回表达式的值
【6】 < > <= >=
两边只要有数字就是对数字进行比较(数字优先），如果都是字符就比较ASCII码
【7】 ==  !=  		-->Number()

※※undefined == null，不等于，不大于，不小于0
※※NaN不等于任何东西包括自己

【8】 不发生类型转换的绝对等于(===)和绝对不等于(!==)
例：
<script type="text/javascript">
	console.log(typeof(undefined）)			//undefined
	console.log(typeof(typeof(undefined)))	//string
	console.log(typeof(NaN))				//number
	console.log(typeof(null))				//object

	var a = "123abc";
	console.log(typeof(+a));				//number
	console.log(+a);						//NaN
	console.log(typeof(!!a));				//boolean
	console.log(!!a);						//true
	console.log(typeof(a+""));				//string
	console.log(a+"");						//123abc
	
	console.log(1 == "1")					//true,因为 == 先经过Number()转换后再进行比较
	console.log(NaN == NaN)					//false
	console.log(NaN == undefined)			//false
	console.log("11" + 11)					//1111
	console.log(1 === "1")					//false
	console.log(parseInt("123abc"))			//123

	var num = 123123.3456789;
	console.log(num.toFixed(3))				//123123.346,因为toFixed(3)的意思是转化为小数点后3位的数(四舍五入）
	console.log(typeof(typeof(a)))			//string
</script>
【9】隐式型转换举例:
<script type="text/javascript">
	x + '';		//等价于String(x)
	+x;			//等价于Number(x)
	x-0;		//等价于Number(x)
	!!x;		//等价于Boolean(x)
</script>
***********************************************************
练习：
<script type="text/javascript">
	var str = false + 1;
	document.write(str);      	//打印1，只要"+"两边没有字符串，就正常为数字相加
	var demo = false == 1;		//先判断false与1是不是相等，把判断后的值赋给demo
	document.write(demo);		//打印false
</script>

<script type="text/javascript">
	if (typeof(a)&& -true + (+undefined) + ""){		//-true返回-1, +undefined返回NaN,(-1 + NaN + ""(空串))返回字符串类型的"NaN",
													//因为从左往右计算，-1 + NaN = NaN, NaN + "" = "NaN"
		docunment.write("基础扎实");					//typeof(a)返回字符串类型的"undefined",然后最后才计算("undefined" && "NaN")(因为逻辑运算符优先级最低),
	}												//注意这两个都是字符串类型的"undefined"和"NaN",所以返回的是true

	if(11 + "11" * 2 == 33){						//优先级： * > + > == ；所以先计算'11' * 2 = 22,再计算11 + 22 = 33
		docunment.write("基础扎实");
	}
	!!" " + !!"" - !!false || document.write("你觉得能打印，你就是猪");
</script>

<script type="text/javascript">
	var b = 1;
	function outer(){
		var b = 2
		function inner(){
			console.log(b);		//undefined
			b++;
			console.log(b);		//NaN
			var b = 3;
			console.log(b);		//3
		}
		inner();
	}
	outer();
</script>

*************************************
*                                  	*
*		valueOf()与toString()      	*
*                                  	*
*************************************
一、valueOf()方法
在类型转换中，经常用到方法valueOf()和toString(),所有对象（包括基本包装类型）都拥有这两个方法,valueOf()方法会将对象的类型转换为基本类型，如果无法转换为基本类型，则返回原对象。
【1】基本包装类型——Boolean型
例：
<script type="text/javascript">
   var obj = new Boolean(true);
   console.log(obj.valueOf());			//true
   console.log(typeof obj.valueOf());	//boolean
   										//如果是包装类型的基本类型，则返回原基本类型值
   var a = true;
   console.log(a.valueOf());			//true
   console.log(typeof a.valueOf());		//boolean

										//如果是基本包装类型对应的基本类型，valueOf()方法会返回原值。
										//但这并不代表基本类型拥有valueOf()方法(基本类型不是对象,不拥有任何方法)
										//而是在读取一个基本类型值时，后台会创建一个对应的基本包装类型的对象
										//从而调用一些方法。所以,基本类型'调用'valueOf()方法时
										//实际上是先创建了一个对应的基本包装类型，由此基本包装类型调用valueOf()
										//最后返回了其对应的基本类型
										//看起来就好像是基本类型调用了valueOf()方法而得到了原始值。
	//Array类型
	var a = [1];
   	console.log(a.valueOf());			//[1]
   	console.log(a === a.valueOf());		//true
   	//函数Function类型（返回原函数）
   	var a = function(){};
   	console.log(a.valueOf());			//function(){};
   	console.log(a === a.valueOf());		//true
   	//正则RegExp类型（返回原正则对象）
   	var a = /a/g;
   	console.log(a.valueOf());			///a/g
   	console.log(a === a.valueOf());		//true
   	//Date类型（返回表示当前时间的数值）
   	var obj = new Date();
   	console.log(obj);								//Wed May 10 2017 12:19:05 GMT+0800 (中国标准时间)
   	console.log(obj.valueOf());						//1494389910179
   	console.log(obj === obj.valueOf());				//false
   	console.log(obj.getTime() === obj.valueOf());	//true
   	//Number类型，如果用整数调用时，要加上括号，否则会报错。因为整数后面的点会识别为小数点。浮点型不会报错。
   	console.log(123.valueOf());			//Uncaught SyntaxError
   	console.log((123).valueOf());		//123
   	console.log(12.3.valueOf());		//12.3
</script>
小结：
	※1）undefined和null没有此方法(基本类型肯定没有方法,String、Number和Boolean是因为有对应的基本包装类型,才可以调用方法);

	2）基本包装类型和对应的基本类型，调用valueOf()返回对应的基本类型值；
	3）对象类型（除Date类型）返回原对象；
	4）Date类型返回表示日期的毫秒数

二、toString()方法
toSting()方法返回该对象的字符串表示
【1】基本包装类型——Boolean型
例：
<script type="text/javascript">

	var obj = new Boolean(true);
   	console.log(obj.toString());		//"true"
   	console.log(typeof obj.toString());	//string
   										//如果是包装类型的基本类型，则返回原基本类型值
    var a = true;
   	console.log(a.toString());			//"true"
   	console.log(typeof a.toString());	//string
   										//如果是基本包装类型对应的基本类型，会返回原值。
   										//但这并不代表基本类型拥有toString()方法(基本类型不是对象，不拥有任何方法)
   										//而是在读取一个基本类型值时，后台会创建一个对应的基本包装类型的对象
   										//从而调用一些方法,所以，基本类型“调用”toString()方法时
   										//实际上是先创建了一个对应的基本包装类型
   										//由此基本包装类型调用toString()最后返回了其对应的字符串
   										//看起来就好像是基本类型调用了toString()方法而得到了对应的字符串。
</script>

【2】基本包装类型——Number型
例：
<script type="text/javascript">
   	var obj = new Number("123");
   	console.log(obj.toString());		//123
   	console.log(typeof obj.toString());	//string
   										//如果是包装类型的基本类型，则返回原基本类型值
   	var a = 123;
   	console.log(a.toString());			//123
   	console.log(typeof a.toString());	//string
   										//同【1】,Number基本包装类型和基本类型调用toString()方法都返回对应的字符串
</script>

注意，如果直接用整数调用时，要加上括号，否则会报错。因为整数后面的点会识别为小数点。浮点型不会报错。
例：
<script type="text/javascript">
	console.log(123.toString());		//Uncaught SyntaxError
   	console.log((123).toString());		//"123"
   	console.log(12.3.toString());		//"12.3"
</script>
此外,数字类型的toString()方法可以接收表示转换基数(可选,2-36中的任何数字),如果不指定此参数,转换规则将是基于十进制.
例：
<script type="text/javascript">
	var n = 33;
	console.log(n.toString());	//'33'
	console.log(n.toString(2));	//'100001'
	console.log(n.toString(3));	//'41'
	console.log(n.toString(10));//'33'
	console.log(n.toString(16));//'21'
	console.log(n.toString(37));//Uncaught RangeError: toString() radix argument must be between 2 and 36
</script>

【3】Date类型（返回表示当前时间的字符串）
例：
<script type="text/javascript">
   	var obj = new Date();
   	console.log(obj);					//Wed May 10 2017 18:20:05 GMT+0800 (中国标准时间)
   	console.log(typeof obj);			//object
   	console.log(obj.toString());		//"Wed May 10 2017 18:20:05 GMT+0800 (中国标准时间)"
   	console.log(typeof obj.toString());	//string
</script> 

※※【4】对象Object类型及自定义对象类型（返回[object Object]）
例：
<script type="text/javascript">

	var obj = {a:1};
   	console.log(obj.toString());		//"[object Object]"
   	console.log(typeof obj.toString());	//string
   	function Foo(){};
   	var foo = new Foo();
  	console.log(foo.toString());		//"[object Object]"
   	console.log(typeof foo.toString());	//string

</script>
在判断对象的类型时，用Object.prototype.toString()返回字符串“[object 对象类型]”，但无法判断自定义对象的类型。

【小结】：
1）undefined和null没有此方法(基本类型肯定没有方法,String、Number和Boolean是因为有对应的基本包装类型,才可以调用方法)
2）Date类型返回表示时间的字符串；
3）Object类型返回字符串“[object Object]”。

【toString()与valueOf()对比】
1）toString()和valueOf()的主要不同点在于，toString()返回的是字符串，而valueOf()返回的是原对象
2）由于undefined和null没有构造函数，所以它们toString()和valueOf()两个方法都没有
3）数值Number类型的toString()方法可以接收转换基数，返回不同进制的字符串形式的数值；而valueOf()方法无法接受转换基数
4）时间Date类型的toString()方法返回的表示时间的字符串表示；而valueOf()方法返回的是现在到1970年1月1日00:00:00的数值类型的毫秒数
5）包装对象的valueOf()方法返回该包装对象对应的原始值

【toString()与转型函数String()函数的对比】
toString()和String()都是将数据转换为对应的字符串，有如下区别：
1）String()可以将任何类型的值转换为字符串，包括undefined和null:
<script type="text/javascript">
   console.log(String(null));		//"null"
   console.log(String(undefined));	//"undefined"
</script>
2）String()不能接受数值基数作为参数
*********************************************
*                函数         				*
*  函数的定义方式、函数的实参与形参、arguments	*
*                        					*
*********************************************
一、函数的两种定义方式：
【1】直接声明
	function theFirstName(){...}
【2】函数表达式
【2.1】命名函数表达式
<script type="text/javascript">
	var test = function abc(){
		document.write("a");
	}
</script>
※※注意：这里面函数abc是不存在的,函数abc只充当了一个表达式,并不是函数名,写了也没有用,需要执行test()才能打印a,
如果执行abc()会报错(Uncaught ReferenceError: abc is not defined)但是test.name = abc
【2.2】匿名函数表达式(由于这种方式比较常用，直接就被称为⁮函数表达式)
<script type="text/javascript">
	var demo = function(){
		document.write("b");
	}
	//这里demo.name = demo, 这是命名函数与匿名函数的区别所在
</script>
写在函数表达式内的函数名将会被忽略
例：
<script type="text/javascript">
	var h = function a(){			//这是一个函数表达式，函数表达式会忽略函数名
		return 23;
	}
	console.log(typeof a());		//报错 	Uncaught ReferenceError: a is not defined, 	这里a是undefined,所以a执行会报错
									//但如果console.log(typeof(a))会打印undefined
</script>
【3】函数不同定义方式对函数命名属性的影响:
<script type="text/javascript">
	function foo(){}			//函数声明
	var bar = function(){};		//函数表达式
	var baz = function baz(){};	//命名表达式

	console.log(foo.name);		//打印 	'foo'
	console.log(bar.name);		//打印 	'bar'
	console.log(baz.name);		//打印 	'baz'
</script>
函数里return后面的语句将不被执行

二、函数名加括号与不加括号的区别：
JavaScript对象方法的定义与调用：
【1】对象的方法定义了一个函数，并作为对象的属性存储,参照js函数定义,函数可以通过声明定义,也可以是一个表达式。
【1.1】函数声明：
<script type="text/javascript">
	function functionName(parameters) {  
	    //code
	}  
</script>
【1.2】函数表达式：
函数表达式存储在变量中，变量可作为一个函数使用，实际上就是一个匿名函数。
<script type="text/javascript">
	var x = function (a, b){
		return a * b;}  
	var z = x(4, 3);  
</script>
总结一下：“functionName”既是函数名，又是一个函数表达式，加上括号表示立即调用该函数。
例(以下两种函数的定义方式其实是一样的):
<script type="text/javascript">
	var test = function() {  
    	return "test"; 
    	} 

	function test() {  
	    return "test";  
	}   
</script>
【2】对象的方法通过添加()调用
显然，对象的方法定义了一个函数，但是并没有调用，如果加上()，则函数会立即被调用。
例：如下函数：
<script type="text/javascript">
	function test() {  
	    return "test";  
	}  

	console.log(test);		//打印 function test() {return "test";} 	函数表达式
	console.log(test());	//打印 test  						   	函数执行结果
</script>
三、函数的实参与形参、arguments:
每次传入实参时，实参会被放入实参列表arguments[ ](数组类型)中
实参长度：arguments.length
形参长度：函数名.length

※※函数的实参列表与对应的形参是一一对应的映射关系，形参赋值改变后，对应的arguments[ ]也随之改变，但这两个值在内存空间里对应的是两个不同的地址
例：
<script type="text/javascript">
	function sum(a,b){
		arguments [1,2];  		//相当于var a = 1
		arguments[0] = 3;
		console.log(a);			//输出3
	}
</script>
实参一旦传入就确定了arguments[]数组的长度，函数里面再赋值形参也没有用了
例：
<script type="text/javascript">
	function sum(a,b){
		b = 2;
		console.log(arguments[1]);
	}
	sum(1);  					//打印的是undefined，这里实参传入了1，则arguments[]里只有1个元素1（arguments[0]）
								//且长度已经确定了，无法再改变b（arguments[1]的值）
</script>
但如果函数内部指定了arguments[]内某个元素的值,则可以强制加入
例：
<script type="text/javascript">
	function sum(a,b){
		arguments[1] = 3;
		console.log(arguments[1]);
	}
	sum(5);  					//打印的是3
</script>
综上可以看出在函数体内部,想通过给函数的实参列表arguments里指定的元素直接赋值的方式改变实参值,在任何时候都可以;
但在函数体内部想通过给函数形参名赋值的方式改变实参值，则必须有相应的实参传入才行。

四、函数声明只能出现在'程序代码'中,这表示他仅能在其他函数体内部或全局空间中,他们的定义不能分配给变量或属性,也不能以参数形式出现在函数调用中。
例(以下3个函数都仅能做函数表达式用,不可做=函数声明用):
<script type="text/javascript">
	// 这是一个函数表达式
	// 他作为参数传递给函数'callMe'
	callMe(function(){
		// 这里是命名函数表达式
		// 也被称为匿名函数
	});

	// 这是命令函数表达式
	callMe(function me(){
		// 这里是命名函数表达式,
		// 并且其名称为'me'
	});

	// 另一个函数表达式
	var myobject = {
		say：function(){
			// 这里是函数表达式
		}
	};
</script>

JavaScript中形参的个数严格意义上来讲只是为了方便在函数中的变量操作,实际上实参已经存储在arguments对象中了
【1】arguments对象是一个比较特别的对象,实际上是当前函数的一个内置属性。
arguments非常类似Array,但实际上又不是一个Array实例,可以通过如下代码得以证实(在函数funcArg中,调用arguments是不必要写成funcArg.arguments,直接写arguments即可)。
<script type="text/javascript">
	Array.prototype.testArg = "test";
	function funcArg() {
	    console.log(funcArg.arguments.testArg);  
	    console.log(funcArg.arguments[0]);
	}
	console.log(new Array().testArg); 	//打印 	test,	执行这步是为了试验一下刚刚写入的Array.prototype.testArg方法是不是好使,打印了test,证明好使
	funcArg(10);                		//打印 	undefined,	 执行这步的结果表示无法调用arguments.testArg方法,证明arguments并不是Array的实例
										//打印 	10
</script>
arguments是类数组,typeof(arguments)返回object:
<script type="text/javascript">
	function test(){
		console.log(typeof(arguments));	//打印 object
	}
	test();
</script>
【2】arguments对象的长度是由实参个数而不是形参个数决定的。
形参是函数内部重新开辟内存空间存储的变量,但是其与arguments对象内存空间并不重叠。
对于arguments和实参值都存在的情况下,两者值是同步的,但是针对其中一个无值的情况下,对于此无值的情形值不会得以同步,如下代码可以得以验证:
<script type="text/javascript">
	function f(a, b, c){
	    console.log(arguments.length);   //打印 	2
	    a = 100;
	    console.log(arguments[0]);       //打印 	100
	    arguments[0] = "qqyumidi";
	    console.log(a);                  //打印 	qqyumidi
	    console.log(c);                  //打印 	undefined
	    c = 2012;
	    console.log(arguments[2]);       //打印 	undefined
	}
f(1, 2);
</script>
arguments.callee: 传入的实参指向函数体自己(arguments的属性)
func.caller:被调用的函数指向调用自己的函数(函数的属性)
例：
<script type="text/javascript">
	function test(){
		console.log(arguments.callee == test);			//打印 	true
	}
	test();
</script>
立即执行函数没有函数名,在想调用自己时就需要用到callee(比如递归)
例1:
<script type="text/javascript">
	//阶乘方法1
	var RecursiveFac1 = function(n){
		if (n == 1){
			return 1;
		}
		return n * arguments.callee(n-1);
	}
	//阶乘方法2
	var RecursiveFac2 = function(n){
        var result = 1;
        for (var i = 1; i <= n; i++)
        {
            result = result * i;
        }
        return result;
    }
	//控制台输入 num(5)			//输出720
</script>
例2:
<script type="text/javascript">
	function test(){
		demo();
	}
	function demo(){
		console.log(demo.caller);//打印 	test函数体ƒ test(){ demo(); }
	}
	test()
</script>
***************************
*                         *
*       立即执行函数       *
*                         *
***************************
【1】立即执行函数,执行后立即被释放，立即执行函数没有名字，其实也可以给起名字，只不过就算起了以后再调用也没用，因为执行完之后就被释放了
【2】针对初始化功能的函数,可以使用立即执行函数
【3】只有表达式才能被执行，执行符号就是(),声明不可以被执行，一个表达式被执行，就会自动忽略表达式的名字
格式：
第一种.(function (形参){}(实参));wc3建议第一种
第二种.(function (形参){})(实参);
例：
<script type="text/javascript">
	(function(a){  
		console.log(a);  
	})(123);  			//打印"123"

	(function(a){  
	        console.log(a);  
	}(1234));  			//打印"1234"

	!function(a){  
	        console.log(a);  
	}(12345);  			//打印"12345"

	+function(a){  
	        console.log(a);  
	}(123456);  		//打印"123456"

	-function(a){  
	        console.log(a);  
	}(1234567);  		//打印"1234567"
</script>
也就是说只有函数表达式才能实现立即执行。匿名函数也是函数表达式为何不能立即执行呢，因为匿名函数开始的function会被JavaScript引擎识别为函数声明的开始，
所以加上括号也不会被执行了，而加上()，！，+，-等符号为什么就可以了呢，因为加上这些符号就可以告诉JavaScript引擎这不是函数声明了，就这么简单。
************************************************************
初始化立即执行函数:
例：
<script type="text/javascript">
	var num = (function (a,b){	//使用num接收立即执行函数的返回值
		return a + b;
	}(1,2))
</script>
****************************************************
函数表达式被立即执行后再去访问表达式就是undefined了
例1:
<script type="text/javascript">
	var demo = function(){
		console.log("a");	//打印 	a
	}();					//函数表达式被立即执行,打印a,执行后函数立即被销毁。
							//demo被赋的是函数的返回值,而函数function里又没有返回值,所以demo就成了undefined，
	console.log(demo);		//打印undefined 	,如果不加()立即执行服的话,demo会被定义(赋值)为function(){...}
</script>
例2:
<script type="text/javascript">
	(function () {
  		var a = b = 0		//var a = b = 0,相当于b = 0;var a = b;b没有用var声明,所以是全局的,而a被声明了,所以是立即执行函数内的局部变量,
  							//立即执行函数执行后会立即被销毁,a也就随之被销毁,不存在了。
	})();
	console.log(b);			//打印 	0		
	console.log(a);			//报错 	Uncaught ReferenceError: a is not defined
</script>
立即执行函数里,出现闭包的话,可以访问外部作用域的变量或参数:
<script type="text/javascript">
	(function(x){
		return (function(y){
			console.log(x);
		})(2)				//打印 1
	})(1);
</script>
*************************
*                       *
*         预编译      	*
*                       *
*************************
JavaScript运行三部曲：
【1】语法分析（先通篇扫描有没有语法错误）
【2】预编译
【3】解释执行（解释一行执行一行）

预编译的表现形式为：
【1】函数声明整体提升
【2】变量声明提升
例：
<script type="text/javascript">
	console.log(a)         //未声明a，直接console.log,直接报错
</script>
<script type="text/javascript">
	console.log(a);        //先调用console.log，后声明a，不报错，打印undefined
	var a = 123;		
</script>

Imply Global暗示全局变量：
	即任何变量，如果未声明就直接赋值，此变量就为全局变量所有(注意这里是变量赋值，不是使用变量，如果未声明直接使用，会报错)全局对象就是window
window:
	window是一个对象,一切声明的全局变量全是window的属性,window就是全局的域
例：
<script type="text/javascript">
	window.a = 10; 		//相当于	window{
						//			a : 10;
						//		}

	var a = 123;		//相当于window.a = 123;

	console.log(a)		//相当于console.log(window.a)
</script>
预编译发生在函数执行的前一刻,预编译过程(预编译四部曲)：
例1：
<script type="text/javascript">
	function fun(a){
		console.log(a);		//输出1
		var a = 123;        //声明a，赋值123
		console.log(a);		//输出2
		function a (){}     //函数声明
		console.log(a);		//输出3
		var b = function(){}//函数表达式（注意这个不是函数声明）
		console.log(b);		//输出4
		function d(){}      //函数声明
	}
	fun(1);
	//打印 function a(){}	输出1
	//打印 123				输出2
	//打印 123				输出3
	//打印 function (){}		输出4
</script>
【1】创建AO对象 (Activation Object)(活跃对象/作用域/执行期上下文）
AO{
}
【2】找形参和变量声明，将变量和形参名作为AO属性名挂起，值为undefined，已经有的不再重复挂
AO{
	a：undefined;
	b: undefined;
}
【3】将实参值和形参统一
AO{
	a：1;
	b: undefined;
}
【4】在函数体里面找函数声明，值赋予函数体，将函数声明的名作为AO属性的名挂起来，已经有的不再重复挂,如果之前已经有值，就覆盖
AO{
	a: function a(){};	这里把第三步的a:1覆盖了
	b: undefined;
	d: function d(){};
}
AO生成完毕，之后执行函数，其实就是到AO里来找各变量
<script type="text/javascript">
	function fn(a){
		console.log(a);        	//这里a为function a(){}（上AO里找）
		var a = 123;           	//声明a已经执行过，直接给a赋值123
		console.log(a);	       	//打印123
		function a (){}        	//函数声明，已经提升过，不执行
		console.log(a);			//继续打印123	
		var b = function(){}  	//函数表达式给b，这时b变为function(){} 
		console.log(b);			//打印b，function(){} 
		function d(){}         	//函数声明
	}
	fun(1);
</script>
**********************************************************************************
例2：								例3：
<script type="text/javascript">
function test(a,b){                 function test(a,b){
	console.log(a);                    	console.log(a);			//function a(){}
	c = 0;                              console.log(b);			//undefined
	var c;                 		        var b = 234;
	a = 3;                              console.log(b);			//234
	b = 2;								a = 123;
	console.log(b);						console.log(a);			//123
	function b(){}   					function a(){}			//函数声明
	function d(){}						var a;
	console.log(b);						b = 234;
}					 					var b = function (){}	//函数表达式
test(1);								console.log(a);			//123
										console.log(b);			//function (){}
									}
									test(1);
	//打印 1								//function a(){}
	//打印 2								//undefined
	//打印 2								//234
										//123
										//123
										//function (){}
</script>
						
第一步、第二步.AO{              			第一步、第二步.AO{
	a:undefined,							a:undefined,
	b:undefined,        					b:undefined;
	c:undefined;						}
}
第三步.AO{								第三步.AO{
	a:1,  									a:1,
	b:undefined,							b:undefined;
	c:undefined;                    	}
}
第四步.AO{								第四步.AO{
	a:1,									a:function(){},
	b:function b(){},						b:undefined;
	c:undefined,						}
	d:function d(){};
}
******************************************************************
全局变量也一样，只不过生成了一个叫window === GO（globle object)的域，然后没有“实参值和形参统一”这一步
执行JavaScript时先生成GO后生成AO
例1：
<script type="text/javascript">
	function test(){
		var a = b = 123;          //此函数相当于先把123赋值给b(赋值语句从右向左执行)，b未声明直接赋值提升为全局变量，然后声明a，再把b的值给a，所以现在全局变量window里有b，没a
		console.log(window.a)     //打印undefined(因为打印的是window.a所以是undefined,但如果打印a就显示123)
		console.log(window.b)     //打印123
	}
	test();
</script>

******************************************************************
例2：
<script type="text/javascript">
	console.log(test);       	//function test(){...}
	function test(test){
		console.log(test); 		//先赋值1，后被function test(){}覆盖
		var test = 234;
		console.log(test); 		//234
		function test(){
		}
	}
	test(1);
	var test = 123;
</script>

先生成GO{
	test : function(test){...}	//由于后面有var test = 123语句，所以test先是undefined后被function(){}覆盖
}

执行test(1)之前先预编译生成AO
AO{
	test : function(){},		//先是undefined，然后被赋值1，最后被function test(){}覆盖
}
***************************************************************************
拓展：									
<script type="text/javascript">
	例1									    例2
	console.log(test());   ...............	console.log(test(2));  			//console.log(test())
	function test(test){					function test(test){
		console.log(test); ...............	    console.log(test);			//console.log(test)1
		var test = 234;							var test = 234;
		console.log(test); ...............  	console.log(test);			//console.log(test)2
	}											return 3;
	test(1);								}
	var test = 123;							test(1);				
											var test = 123;	
</script>
则打印										则打印	//由于console.log(test())里有test()语句，所以要执行test()
undefined	...........................    	2     	//是console.log(test)1的,此时正在执行test()
234			...........................		234		//是console.log(test)2的,此时正在执行test()
undefined	...........................		3		//这一步打印的是test()函数的返回值，由于例1里函数内无返回值所以是undefined，例2里有返回值return 3，所以打印3，此时正在执行test()
1			...........................		1		//是console.log(test)1的,此时正在执行test(1)
234			...........................		234		//是console.log(test)2的,此时正在执行test(1)

例3：
<script type="text/javascript">
	global = 100;
	function fn(){
		console.log(global);  	//undefined,原因：GO {global:100},但AO里有global参数，此时AO{global:undefine},先找AO后找GO
		global = 200;
		console.log(global); 	//200
		var global = 300;
	}

	fun();
	var global;
</script>
例4(先执行预编译然后才执行函数):
<script type="text/javascript">
	//GO{
	//	a:undefined
	//}
	function test(){
		console.log(b);			//undefined,这里打印b不会报错而是返回了undefined是因为有了if(a){var b = 100;}语句的存在。
								//这条语句无论是否执行都算是对b进行了声明(在此函数中确实未执行),但b也被声明了
								//产生这种现象就是因为预编译是在函数执行前发生的，在预编译时函数内部的所有声明整体被提升，这时函数还未执行。
		if(a){					//先找AO里有没有a，这里AO里没有a，所以找GO里的a，此时GO里的a:undefined,因为是预编译，还没执行到a = 10语句
			var b =100;			//由于a是undefined，所以这条语句不被执行
		}
		console.log(b)			//undefined
		c = 234;
		console.log(c);			//先找AO里有没有c，这里AO里没有c，所以找GO里的c，此时GO里的c:234，打印234
	}
	var a;
	test();
	//AO{
	//	b:undefined
	// }
	a = 10;
	console.log(c);				//234
</script>
***********************************************************************************
百度题：
【1】
<script type="text/javascript">
	function bar(){
		return foo;
		foo = 10;
		function foo(){	
		}
		var foo = 11;	
	}
	console.log(bar());    		//打印ƒ foo(){}	,因为预编译发生在函数执行前，执行return foo;语句之前foo就已经被声明了。
</script>
【2】
<script type="text/javascript">
	console.log(bar());
	function bar(){
		foo = 10;
		function foo(){
		}
		var foo = 11;
		return foo;
	}							//打印11
</script>
例5：
<script type="text/javascript">
	a = 100;
	function demo(e){
		function e(){}
		arguments[0] = 2;        	//实参变为2，形参跟着变为2，此时e=2
		console.log(e);				//打印2
		if (a){						//此时a为undefined，if里的语句不执行，
									//注意如果a为一个函数的话也算true，if里的语句也会执行
			var b = 123;
			function c(){}			※※//if里不能声明function，声明了外面也不被编译与执行
		}
		var c;
		a = 10;
		var a;
		console.log(b);				//由于if(a)里声明了var b，所以打印undefined，如果直接写b = 123，而未进行声明则报错
		f = 123;					//这里f = 123直接升格为GO变量
		console.log(c);				//打印undefined
		console.log(a);				//打印10
	}
	var a;
	demo(1);
	console.log(a);					//打印100，因为GO里a为100,但如果function demo(e){...}里没有var a语句的话,则a就变成了全局变量，所以这里会打印10(因为function demo(e){...}有a = 10语句)
	console.log(f);					//打印123，因为GO里f为123，如果前面的f = 123改为var f = 123，则这句报错
</script>
***************************
*                         *
*   作用域链、执行期上下文  *
*                         *
***************************
[[scope]]:
每个javascript函数都是一个对象，对象中有些属性我们可以访问（比如函数名），但有些不可以，这些属性仅供javascript引擎存取，[[scope]]就是其中一个;
[[scope]]指的就是我们所说的作用域，其中存储了执行期上下文的集合。
执行期上下文(AO):
一个执行期上下文定义了一个函数执行时的环境，函数每次执行时对应的执行上下文都是独一无二的，所以多次调用一个函数会导致创建多个执行上下文，当函数执行完毕，它所产生的执行上下文就被销毁
查找变量:
在哪个函数里查找变量，就在哪个函数作用域链的顶端依次向下查找
作用域链：
[[scope]]中所存储的执行期上下文对象的集合，这个集合呈链式链接，我们把这种链式链接叫做作用域链
函数的作用域就是函数的一个属性
<script type="text/javascript">
	function a(){}
	var glob = 100;
	a();

	//a defined a.[[scope]] --> {0 : GO{}}
	//a doing   a.[[scope]] --> {0 : AO{},
	//                           1 : GO{}}
</script>
********************************************
例：
<script type="text/javascript">
	function a(){  				//a defined
		function b(){			//b defined
			function c(){		//c defined
			}
			c();				//c doing
		}
		b();					//b doing
	}
	a();						//a doing
</script>
执行步骤：
a defined a.[[scope]] -- > 	{0 : GO} 	//此时GO里{a:function a(){ ...}}，其实这里GO里有{a:function a(){ ...}}，是因为a被defined(声明)了 

a doing   a.[[scope]] -- > 	{0 : aAO, 	//函数执行前先预编译生成AO(a的AO,aAO),此时aAO里{b:function b(){ ...}}，
			   				 1 : GO}	//其实这里aAO里有{b:function a(){ ...}}，是因为在函数a里b被defined(声明)了

b defined b.[[scope]] -- > 	{0 : aAO,	//此时的aAO与a doing时的aAO是同一个东西（这一步相当于把a doing时的0 : aAO和1 : GO一起搬过来，当成了自己的GO
			   				 1 : GO}	//其实这一步可以理解成和上一步是重复的：正是由于这里有b defined，才有上一步的aAO里有{b:function a(){ ...}}

b doing   b.[[scope]] -- > 	{0 : bAO,	//函数执行前先预编译生成AO(b的AO,bAO),此时bAO里{c:function b(){ ...}}，因为下边c被defined(声明)了
			   				 1 : aAO,	//继承上一步，此时的1 : aAO和2 : GO相当于自己的GO
			   				 2 : GO}

c defined c.[[scope]] -- > 	{0 : bAO,	//相当于重复了，直接拿上一步的劳动成果
			   				 1 : aAO,
			   				 2 : GO}

c doing   c.[[scope]] -- > 	{0 : cAO,
			   				 1 : bAO,	//1 : bAO，2 : aAO，和3 : GO加在一起相当于c自己的GO
			   				 2 : aAO,
			   				 3 : GO}
总结：
以后找函数的作用域链只要看函数自己doing时的作用域链就行了，不用看下个函数的defiend
例：
<script type="text/javascript">
	function a(){
		var num = 100;
		function b(){
			num ++;
			console.log(num);	//console.log(num)1
		}
		console.log(num);		//console.log(num)2
		return b;	
	}
	var demo = a();	//打印100,调用a函数,先执行console.log（num)2,然后返回的是函数名为b的一个对象（也就是函数b),a调用之后自己的AO就被销毁了num = 100随之销毁
					//此时相对于function b(){...}的GO里num = 100,注意这个num并不被销毁，因为它是相对于function b(){...}的GO里的num
					//销毁的只是AO,GO不会被销毁,这里面销毁的只是a的AO,因为函数b并未执行，所以也就不存在b的AO(如果有的话执行后也立即销毁)
					
	demo();			//打印101，注意上一条语句是a()执行后把对象名为b的对象赋值给demo，而这条语句是调用demo(也就是函数b)，
					//一个有括号，一个没括号,有括号的是执行函数，没括号的是赋值语句(这里赋值成名为b的函数型对象了)
					//先执行num++，num变成101,然后执行console.log(num)，注意,这里b的AO里没有num,所以上b的GO里找,num = 101,执行后b的AO销毁，但b的GO保持不变
					
	a();			//打印100，a函数又被调用,先执行console.log（num)2,此时num = 100(因为上次执行a后a的num伴随自己的AO一起被销毁了)
					//然后又返回一个函数名为b的一个对象(也就是函数b)，但这里并没有把b赋值给demo(也就是没有东西接收新生成的返回值)
					//然后返回的b和新生成的num = 100一并伴随a的AO一起被销毁了

	demo();			//打印102，由于上一句并没有把返回值b赋值给demo，demo里的AO和GO依旧和未执行上一句前一样

	a();			//打印100，a函数调用后返回的是函数名为b的一个对象（也就是函数b）,依旧没有把b赋值给demo，同时console.log（num）,此时num = 100
</script>
*************************
*                       *
*         闭包        	*
*                       *
*************************
当内部的函数被保存到外部时就会生成闭包，闭包会导致原有作用域链不释放,造成内存泄漏
例：
<script type="text/javascript">
	function a(){
		function b(){
			var bbb = 234;
			console.log(aaa);		//先找function b的AO里有没有aaa，发现没有，就上相对于function b的GO里找(也就是function a的AO)，发现aaa = 123
		}							//a的AO里aaa为什么 = 123？ 因为a的AO里先由于aaa被定义,所以aAO{aaa:undefined},然后函数a执行，a执行时aaa = 123，
									//之后才return b，只有函数a执行才能生成函数b，而在生成b之前aaa就被赋值成了123
		var aaa = 123;					
		return b ;					//执行到这一步把b的AO保存起来了，甩到了全局变量里
	}
	var glob = 100;
	var demo = a();					//现在demo被赋值成了b的AO里的属性与方法
	demo();							//执行b, 打印123
</script>
**************************************************
不小心写出的闭包:
下面会打印打印10个10
<script type="text/javascript">
	function test(){						//声明一个函数，名字叫test
		var arr = [];						//声明一个数组，名字叫arr
		for(var i = 0; i < 10; i ++){		//for循环执行10次
			arr[i] = function(){			//每次执行for循环arr[i]都被赋值为function(){},注意这里只是赋值为function(){}，函数并不被执行
				document.write(i + " ");	//至于function(){}里面写的是什么暂时还不知道，等到function(){}被执行时再说
			}					
		}									//当for循环执行结束时arr[1]=function(){},arr[2]=function(){},arr[3]=function(){}....
											//且这些函数共用一个相对于函数体本身的外部全局变量i
		return arr;							//for循环执行完之后i就变成了10
	}
	var myArr = test();						//把test()执行后返回的数组赋值给myArr
	for (var j = 0; j < 10; j++){
		myArr[j]();							//此时才执行每个myArr[]里的function(){},这时相对于每个function(){}函数的外部变量GO里i = 10，
											//而每个function(){}函数的内部并没有声明i，所以上相对于每个function(){}函数的外部变量GO里找i，i = 10
	}										//所以arr[1]=document.write(i + " "),arr[2]=document.write(i + " ")....
											//且所有的i都指向同一个i,此时的i已变为10，打印10个10
</script>
***************************************************
如果想打印
0 1 2 3 4 5 6 7 8 9
则：
<script type="text/javascript">
	function test(){
		var arr = [];
		for(var i = 0;i < 10;i ++){
			(function(j){	..................	//这里就是每次循环都让函数立即执行，其实目的就是让每次立即执行前生成新的AO，随后被立即销毁
												//每次循环执行function(j){...}(i)时(前)都会在相对应的function的AO里生成新的{j: undefined}
				arr[j] = function(){			//每次都生成新的是因为立即执行函数执行后就被销毁了，注意这里销毁的是作为函数体的function(j){...}(i)，
												//而arr[j] = function(){...}作为函数执行结果被保留了下来,不会被销毁
					document.write(j + " ");	//紧接着新立即执行function对应的AO里j的值会被实参i所取代，而每执行完一次后i会+1，此时立即执行function里的AO{j: i}
												//这样就保证了每次执行function(j){...}(i)后函数function(){document.write(j + " ");}所在的GO里，
				}								//也就是function(j){...}(i),的AO里j的值都是不被公用的，且等于每次循环时i的值
			}(i));	........................	//	立即执行标志  //
		}										//函数每次立即执行后被销毁的是function(j){...}(i)这个函数，但函数的执行结果是而arr[j] = function(){..}，函数的执行结果并不会被销毁
		return arr;
	}
	var myArr = test();
	for (var j = 0; j < 10; j++){
		myArr[j]();	........................	//其实每次执行时function(){document.write(j + " ");}的AO里并没有j，他会上自己的GO里找，
												//于是打印了"0 1 2 3 4 5 6 7 8 9"
	}
</script>

打印10个10形成闭包时的function嵌套结构(两层)：				打印0 1 2 3 4 5 6 7 8 9时解决闭包的function嵌套结构(三层):
<script type="text/javascript">
function test(){										function test(){
	...                                               		...
	arr[i] = function(){									(function(j){
		...                                                		arr[j] = function(){
	}                                                      			...
}																}
															})(i)
														}
</script>
正是因为多加了一层function嵌套结构，才能把原来的(指生成闭包时的)相对于每个function(){document.write(i + " ");}函数的外部变量GO里i的值赋给了i的内层函数j,
此时j就变成了相对于function(){document.write(j + " ");}的GO，这样每次执行function(){document.write(j + " ");}所寻找的j，都是i所处的函数内部重新嵌套生成的j，
且j只等于生成j时的i,i再改变也不会影响j的值，而且由于每次的j都是重新嵌套生成的，所以j之间互相独立存在

或:(这种方法只能外部触发,无法调用)														
<script type="text/javascript">
	function test(){						
		var arr = [];						
		for(var i = 0;i < 10;i ++){			
			arr[i] = function(){		
				document.write(i + " ");	
			}();		//-->注意这是一个函数表达式形式的立即执行函数
		}								
		return arr;						
	}
	var res = test();
	console.log(res);	//[undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]				
</script>
********************************************************
当内部函数被保存到外部时，将会生成闭包,闭包会导致原有作用域链不释放，造成内存泄漏。
由于里面函数看到了外面函数的执行成果，并且保存到自己身上，所以这个函数存活的时间比基础函数时间还长，就产生了不该有的效果，于是就产生了闭包。
闭包的本质就是函数的生命周期弄混乱了，闭包还可以有多种表达形式
比如：
<script type="text/javascript">
	var demo;						//声明demo
	function test(){				//声明函数test
		var abc = 100;				//声明abc,然后赋值为100
		function a(){				//声明函数a
			console.log(abc);
		}
		demo = a;					//把函数体a赋值给demo,注意此时demo是全局变量,所以执行这条语句时相当于把函数体a甩到了全局变量里,形成了闭包
	}
	test();							//执行函数test
	demo();							//执行demo相当于执行函数体a
</script>
**********************************************************
闭包作用：
【1】实现变量公有化(在闭包里的GO作用域下变量是公有化的)
函数累加器(不依赖于外部变量并且能反复执行的函数累加器)
例:
<script type="text/javascript">
	function add(){
		var count = 0;
		function demo(){
			count++;
			console.log(count);
		}
		return demo
	}
	var counter = add();
	counter();
	counter();
	counter();
</script>
【2】可以做缓存(存储结构)
例：(多个函数和一个函数形成闭包时，使用公共的外部全局变量，例如下面的例子，每次执行eatFood与pushFood时所使用和修改的food都是同一个food)
<script type="text/javascript">
	function test(){
		var food = "apple";
		var obj = {
			eatFood : function(){
				if (food != ""){
					console.log("I am eating " + food);
					food = "";
				}else {
					console.log("There is nothing!");
				}
			},
			pushFood : function(myFood){
				food = myFood;
			}
		}
		return obj;
	}
	var person = test();		//执行test函数，把返回的obj对象赋值给person
	person.eatFood();			//person对象调用eatFood方法，此时food="apple",执行if(food != "")里的函数
								//打印"I am eating apple"，执行后food = ""
	person.eatFood();			//此时food=""执行else里的函数,打印"There is nothing!"
	person.pushFood("banana");	//person对象调用pushFood方法,执行后food = "banana"
	person.eatFood();			//执行eatFood方法，打印"I am eating banana"
</script>
对同一个num操作的两个函数,下例中add函数与sub函数操作的num都是同一个num：
<script type="text/javascript">
	function test(){
		var num = 100;
		function add(){
			num ++;
			console.log(num);
		}
		function sub(){
			num --;
			console.log(num);
		}
		return [add,sub];
	}
	var myArr = test();
	myArr[0]();					//打印101
	myArr[1]();					//打印100
</script>
【3】私有化变量(在构造函数内部给没有使用this的变量赋值后,这个变量就相当于被私有化了,因为通过被构造的函数无法访问该属性)
例：
<script type="text/javascript">
	function Deng(name,wife){
		var preparewife = "xiaozhang";

		this.name = name;
		this.wife = wife;
		this.divoce = function(){
			this.wife = preparewife;
		}
		this.changePreparewife = function(target){
			preparewife = target;
		}
		this.sayPreparewife = function(){
			console.log(prepareWife);
		}
	}
	var deng = new Deng('deng','xiaoliu');
	//此时控制台输入deng.preparewife会输出undefined,因为preparewife属性并不是以this开头的
	//但在控制台输入deng.divoce、deng.changePreparewife和deng.saypreparewife
	//却可以正常访问和修改deng.preparewife，就好像是preparewife = "xiaozhang"
	//是deng.divoce、deng.changePreparewife和deng.saypreparewife的专有属性，
	//出现这个情况是因为产生了闭包：
	//this.divoce = function(){...}; this.changePreparewife = function(target){...}; this.sayPreparewife = function(){...}
</script>
【4】模块化开发防止污染全局变量
例(init是约定俗成的函数入口的意思):
<script type="text/javascript">
	var name = 'bcd';
	var init = (function(){
		var name = 'abc';
		function callName(){
			console.log(name);
		}
		return function(){
			callName();
		}
	}())
	init();		//执行init函数, 打印'abc'
				//init是一个立即执行函数,执行后返回的结果为function(){callNmae();}
				//所以执行init函数相当于执行function(){callNmae();}，执行这个函数就是执行callName(),
				//执行callName()就是执行console.log(name),打印abc
				//此时外部变量name重新赋值为bcd对init的执行也不会有影响
				//闭包使变量私有化不污染全局变量
</script>
闭包能够访问外部作用域的变量或参数:
<script type="text/javascript">
	(function(x){
		return (function(y){
			console.log(x);
		})(2)
	})(1);		//打印 1
</script>
*************************************************************
课堂练习：
【1】
<script type="text/javascript">
	var num = 100;
	function test(){
		console.log(num);
	}
	num = 200;
	test();				//打印200
</script>
****************************************************
【2】写一个方法,求一个字符串的字节长度，一个中文占两个字节，一个英文占一个字节:
	charCodeAt()方法可返回指定位置的字符的Unicode编码，返回值是0-65535之间的整数。（当返回值<=255时为英文，返回值>255时为中文）
	语法:stringObject.charCodeAt(index)
例:
<script type="text/javascript">
	var str = "Hellow world"
	document.write(str.charCodeAt(1));			//输出101,字母e的Unicode编码编码就是101
</script>
冗余：
<script type="text/javascript">
	function retByteslen1(target){
		var count  = 0;
		for(i = 0; i < target.length; i++){
			if(target.charCodeAt(i) <= 255){
				count++;
			}else if(target.charCodeAt(i) > 255){
				count += 2;
			}
		}
		console.log(count);
	}
</script>
简练：
<script type="text/javascript">
	function retByteslen2(target){
		var count = target.length;
		for(var i = 0; i < target.length; i++){
			if(target.charCodeAt(i) > 255){
				count++;
			}
		}
		console.log(count);
	}	
</script>
再简：
<script type="text/javascript">
	function retByteslen(target){
		var count,
			len;
		count = len = target.length;
		for(var i = 0; i < len; i++){
			if(target.charCodeAt(i) > 255){
				count++;
			}
		}
		console.log(count);
	}	
</script>
课堂讲解笔试题:
<script type="text/javascript">
	var x = 1;
	if(function f(){}){			//if()括号里的function f(){}不为false所以执行if里的语句，而function f(){}两边加了括号所以就不是函数声明了，变成了函数表达式
		x += typeof f;			//f未经声明直接typeof返回的的是字符串类型的"undefined"，x = 1 + "undefined" = "1undefined"
	}
	console.log(x);				//1undefined
	console.log(typeof(x));		//string
</script>
*************************
*                       *
*       私有化变量       * 
*                       *
*************************
【1】使用new方法私有化变量(构造函数内部一些属性和方法不希望被访问所以使用私有化变量)
下例中被构造的对象里money作为一个枢纽值不想被访问只想让被构造的函数访问makeMoney和pay方法
例：
<script type="text/javascript">
	function Person(name){
		//var this = {
		//	makeMoney : function(){}
		//	offer : function(){}
		//	__proto__ : Person.prototype
		//}
		var money = 100;
		this.name = name;
		this.makeMoney = function(){
			money ++;
			console.log(money);
		}
		this.offer = function(){
			money --;
			console.log(money);
		}
		//return this;
	}
	var person = new Person(),
		person1= new Person();

	person.makeMoney();						//打印 	101
	person1.makeMoney();					//打印 	101
	person.makeMoney();						//打印 	102
	console.log(person.money); 				//打印 	undefined, 	money被私有化保护起来,没有使用this属性,所以无法访问,但可以使用
</script>
【2】自己封装继承函数,也可以使用私有化变量:
<script type="text/javascript">
	var inherit = (function(){
		var F = function(){};				//这里面F函数并没有任何用处,只是充当一个媒介传递Origin.prototype,所以被私有化处理了,无法访问
		return function(Target,Origin){
			F.prototype = Origin.prototype;
			Target.prototype = new F();
		}
	}())
</script>
*****************************
*                           *
*      对象、对象的属性     	*
*                           *
*****************************  
一、对象的创建方法:
【1】通过对象字面量/对象直接量(plainObject)方式创建
	var obj = {}  
【2】通过'构造函数'方式创建
	通过'构造函数'方式创建时要在函数名前要加new
【2.1】通过自定义构造函数创建:
创建方法:
	var obj = new 函数名();
通过该方法创建对象时,会自动执行该函数,这点类似于php通过创建对像时,会自动调用构造函数,因此该方法称为通过'构造函数'方式创建对象
人为规定为大驼峰式命名规则比如TheFirstName (theFirstName是小驼峰式命名规则,这里尽量不要用,以免造成混淆)
使用方法：
<script type="text/javascript">
	function Person(){}
	var person1 = new Person();
</script>
例1:
<script type="text/javascript">
	function Student (name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
		this.grade = 2017;
	}
	var student = new Student('张三'，18，'male');
</script>
例2：
<script type="text/javascript">
	function Person() {
	    this.name = "dongjc";    	//通过this关键字设置默认成员
	    var worker = 'coding';    	//没有this关键字,对象创建后,该变量为非成员
	    this.age = 32;
	    this.Introduce = function () {
	        console.log("My name is " + this.name + ".I'm " + this.age);
	    };
	    console.log("My name is " + this.name + ".I'm " + this.age);
	};
	var person = new Person(); 		//打印 	My name is dongjc.I'm 32
	person.Introduce(); 			//打印 	My name is dongjc.I'm 32
	console.log(person);			//打印 	Person {name: "dongjc", age: 32, Introduce: ƒ}
</script>
此代码一共会两次打印'My name is dongjc.I'm 32',原因在于使用new关键字创建对象时自动执行了该函数。
注意：this关键字的使用,这里的this与php中话法意思类似,指调用该函数的对象,这里指的是person
【2.2】通过系统自带的构造函数Object()创建
使用方法：
<script type="text/javascript">
	var obj = new Object();
	obj.name = 'abc';
	obj.sex = "male";
	obj.say = function(){};
</script>
例：
<script type="text/javascript">
	var person = new Object();
	person.name = "dongjc";
	person.age = 32;
	person.Introduce = function () {
	        console.log("My name is " + this.name + ".I'm " + this.age);
	    };
	person.Introduce();    			//打印 	My name is dongjc.I'm 32
</script>
【3】通过create方法创建
	var obj = Object.creat(definedProperty(对象或null));
例：
<script type="text/javascript">
	var demo = {
		lastName : 'Lee'
	}
	var obj = Obj.create(demo);
	//obj = {
	//		__proto__ : demo
	//}
</script>
二、对象的属性:
【1】在给对象添加属性时,其实系统内部自动对属性名使用了toString()隐式型转换方法:
<script type="text/javascript">
	var a = {},
		b = {key : 'b'},
		c = {key : 'c'},
		d = {sun : 'd'};

	a[b] = 123;
	console.log(a[b]);		//打印 	123
	console.log(a);			//打印 	{[object Object]: 123}
	a[c] = 456;
	console.log(a[b]);		//打印 	456
	console.log(a);			//打印 	{[object Object]: 456}
	a[d] = 789;
	console.log(a[b]); 		//打印 	789
	console.log(a);			//打印 	{[object Object]: 789}
</script>
【2】不可配置属性:
隐含全局变量(在函数内部未经声明创建的变量)与明确定义的全局变量有细微的不同,不同之处在于能否使用delete操作符撤销变量
一旦一个变量是使用var创建的全局变量(这类变量在函数外部创建),就变成了不可配置的属性,不可配置的属性delete不掉;不使用var创建的隐含全局变量(尽管他是在函数内部创建)可以删除。
这表明隐含全局变量严格来讲不是真正的变量,而是全局对象的属性。属性可以通过detelete操作符删除,但变量不可以
【2.1】函数的形参变量相当于在函数体内部var这个形参变量,一旦var操作变量后就无法删除
<script type="text/javascript">
	a = (function(b){		//函数形参里有b相当于在函数体内部var b
		delete b;			//变量一旦经历var操作后就无法被delete
		return b;			//b还是1
	})(1);
	console.log(a); 		//打印 	1
	var c = 2;
	delete c;
	console.log(c); 		//打印 	2; 变量c经历了var操作,无法被delete
</script>
【2.2】函数内部使用var创建的变量,在函数内部也无法delete
<script type="text/javascript">
	function test(){
		var d = 3;
		delete d;
		console.log(d); 	//打印 	3; 变量d经历了var操作,无法被delete
		e = 4;
		delete e;
		console.log(e); 	//报错 	Uncaught ReferenceError: e is not defined at test, 	变量e没有经历var操作,可以被delete
	}
	test();
</script>
在ES5 strict模式中,为没有声明的变量赋值会抛出错误。
*****************************
*                           *
*     包装类(基本包装类性)  	*
*                           *
*****************************
【1】为了便于操作“基本类型值”，JS提供了三个特殊的引用类型：Boolean、Number、String，这些类型和其他引用类型相似,但同时也具备与各自基本类型相应的特殊行为。
实际上：每当读取一个基本类型值的时候，“后台就会创建一个对应的基本包装类型的对象”，从能能够调用一些方法来操作这些数据。
【2】引用类型和基本包装类型的主要区别就是对象的生存期;
【3】自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁,这意味着我们不能在运行时为基本类型值添加属性和方法;
例1:
<script type="text/javascript">		//包装类
	var num = 4;
	num.len = 3;					//此时系统新建一个new Number构造函数，把3赋值给num.len属性，然后立即销毁,相当于 new Number(4).len = 3;随即delete 				
	console.log(num.len);			//此时系统又会新建一个new Number构造函数,并访问num.len属性,这个new Numebr已经不再是上面那个new Numebr了,返回undefined
</script>
例2:
误以为输出结果为ab，而实际输出结果为abcd：
<script type="text/javascript">
    var str = "abcd";
    str.length = 2;
    								//实际执行为，new String("abcd"),
    								//然后new String("abcd").length = 2,然后delete销毁这个对象，与前面的对象无关，所以后面访问str时，还是abcd
    console.log(str);
</script>
【4】像基本包装类型这样的简单类型,他们不是引用类型,因为他们不能人为添加属性和方法
以String对象为例：
<script type="text/javascript">
	var box = 'Mr.Xiao';
	box.name = 'xiao';
	box.age = function(){
	        return 10;
	    } 
	console.log(box.name);  		//undefined
	console.log(box.age());  		//报错 	Uncaught TypeError: box.age is not a function 	,因为box里没有age方法,直接调用会报错
</script>
虽然基本包装类型不能人为添加属性和方法,但却可以调用自己的系统内置方法或属性:
以String基本包装类型为例：
<script type="text/javascript">
	var box ='Mr.Xiao';     		//字面量创建方式 
	console.log(box.substring(2)); 	//打印 .Xiao; box是String类型的基本类型,但是也是特殊的引用类型,在后台会创建一个基本包装类型的对象,所以可以调用系统内置的函数
									//.substring()方法是系统给String基本包装类型内置的方法,所以可以调用
</script>
【5】但数字、字符串、布尔类型的变量可以对其进行构造函数处理,之后他就会有属性和方法了:
<script type="text/javascript">
	var num = new Number(123);
	var str = new String('abcd');
	var bol = new Boolean('true');
</script>
例:
<script type="text/javascript">
	var num = new Number(123);
	var str = new String(123);
	var bol = new Boolean(123);
	console.log(num);     									//打印 	Number {123}
	console.log(Object.prototype.toString.call(num));		//打印 	[object Number]
	console.log(str);										//打印 	String {"123"}
	console.log(Object.prototype.toString.call(str));		//打印 	[object String]
	console.log(bol);										//打印 	Boolean {true}
	console.log(Object.prototype.toString.call(bol));		//打印 	[object Boolean]
</script>
使用new修饰符创建的对象是引用类型的数据,可以人为添加属性和方法
以使用new修饰符创建的String对象为例:
<script type="text/javascript">
	var box = new String('abcde');
	box.name = 'xiao';
	box.age = function(){
	       return 10;
	}
	console.log(box);				//打印 	String {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", name: "xiao", length: 5, [[PrimitiveValue]]: "abcde"}
	console.log(box.name);   		//打印 	xiao
	console.log(box.age());   		//打印 	10
	console.log(box.substring(2)); 	//打印 	cde
</script>
*************************************************************
笔试题：
<script type="text/javascript">
	var str = "abc";
	str += 1;
	var test = typeof(str);			//test == "string"
	if (test.legth == 6){
		test.sign = "typeof的返回结果可能为String"
									//new String(test).sign = 'xxx';		
									//随即delete，一定要注意number,string,boolean等原始值没有属性值和方法
	}
	console.log(test.sign);			//重新生成并调用新的new String(test).sign,打印undefined
</script>
****************************************************************
 <script type="text/javascript">
 	function Person(name, height){
	 	this.name = name;
	 	this.height = height;
	 	this.say = function(){
	 		console.log(this.say);
	 	}
	 }
 	console.log(new Person('xiaowang',180).say);	//输出function(){console.log(this.say);}
 </script>
****************************************************************
课堂练习：
<script type="text/javascript">
	function Person(name,age,sex){
		var a = 0;
		this.name = name;
		this.age = age;
		this.sex = sex;
		function sss(){
			a ++;						//这里并没有var a,执行sss时不会在sssAO里生成{a:undefined},而是上他的GO里找a,他的GO里a = 0,然后执行a ++,执行后a变成1
			console.log(a);				//如果把sss函数变成function sss(){document.write(a++);},则会分别打印	//0	//1	//0
		}
		this.say = sss;					//把sss函数返回到this.say属性上,形成闭包
	}
	var oPerson = new Person();
	oPerson.say();						//打印1，由于oPerson函数中this.say = sss,oPerson.say()相当于sss()
	oPerson.say();						//打印2
	var oPerson1 = new Person();		
	oPerson1.say();						//打印1，oPerson1函数中重新生成了新的AO和他对应的GO
</script>
**********************************************************************
百度笔试题：
<script type="text/javascript">
	var x = 1,
		y = z = 0;
	function add(n){
		return n = n + 1;
	} 
	y = add(x);							//打印4,因为写在后面的function add(n){return n = n + 3;}会覆盖写在前面的function add(n){return n = n + 1;}
	console.log(y);
	function add(n){
		return n = n + 3;
	}
	z = add(x); 
	console.log(z);						//打印4
</script>
*******************************************************
阿里巴巴题：
下面代码中console.log的结果是[1,2,3,4,5]的是：
<script type="text/javascript">
	//A
	function foo(x){
		console.log(arguments);
		return x;
	}
	foo(1,2,3,4,5)					//可以
	//B
	function foo(x){
		console.log(arguments);
		return x;
	}(1,2,3,4,5);					//不可以,本题是想考察立即执行函数，立即执行函数的格式:
									//(function(){})()或(function(){}())
									//写成题目中这样不会报错也不会执行系统会看成function foo(x){...}(1,2,3,4,5)
									//但如果写成function foo(x){...}()会报错
	//C
	(function foo(x){
		console.log(arguments);
		return x;
	})(1,2,3,4,5)					//可以，立即执行函数
	//D
	function foo(){
		bar.apply(null,arguments)	//这个意思是不改变this指向，和bar(arguments)是一样的
	};								//相当于执行了bar([1,2,3,4,5]),向bar里传入了实参数组
	function bar(){
		console.log(arguments)
	}
	foo(1,2,3,4,5);					//可以
</script>
******************************
*                            *
*       construct构造器		 *
*                            *
******************************
构建一个构造函数时会同时产生一个该构造函数的原型,该原型表现形式上为空对象，但实际上系统默认给添加了construct和__proto__属性，construct指向自己的函数体本身，
这样做的目的是在这个构造函数构造出的对象内调用construct会指向自己的构造函数(让这个对象知道是谁构造出的自己)。
所以：
<script type="text/javascript">
	function Car(){}
	var car = new Car();
	//控制台输入：
	//car.constructor  	输出 function Car(){}
	//Car.prototype 	输出
	//					Object{
	//						constructor : Car()		构造函数Car的原型上的constructor指向自己,也就是function Car(){...};
	//												目的就是让Car构造出的所有新对象能通过.constructor属性找到自己的构造器function Car(){...}.
	//						__proto__ : Object		构造函数Car的原型是Object
	//					}
</script>
constructor属性可以手动更改
例：
<script type="text/javascript">
	function Person(){
	}
	Car.prototype = {
		constructor : Person
	}
	function Car(){
	}
	var car = new Car();
	//此时控制台输入car.constructor则会输出function Person(){}
</script>
*****************************
*                           *
*       构造函数内部原理     	*
*                           *
*****************************
在函数名前加 new 触发构造函数机制:
如果没有new就当正常函数执行,有的话则：
【1】在函数体最前面隐式的加上this空对象(创建一个空对象this = {})
【2】执行this.xxx = xxx;
【3】隐式的在末尾处增加return this;但可以在构造函数末尾处手动强制return一个其他对象,这个强制返回的对象将会覆盖原来隐式返回的this对象
但一定要注意,这里只能是返回对象类型的数据,如果返回其他类型的数据将会被自动忽略,例：
<script type="text/javascript">
	function Student(name,age,sex){
		//var tihs = {
		//	name : ""
		//	age : ""
		//};
		this.naem = name;
		this.age = age;
		this.sex = sex;
		this.grade = 2017;
		//return this; 	此时构造函数new Student与this对象指向同一个对象
	}
	var student = new Student('张三'，18，'male');
</script>
 <script type="text/javascript">
 	function Person(name, height){
	 	this.name = name;
	 	this.height = height;
	 	this.say = function(){
	 		console.log(this.say);
	 	}
	 }
 	console.log(new Person('xiaowang',180).name);	//打印"xaiowang"
 </script>
模拟构造函数:
<script type="text/javascript">
	function Person(name,height){
		var that = {};
		that.name = name;
		that.height = height;
		return that;
	}
	var person = Person("xiaoming",180);
	console.log(person)						//Object {name: "xiaoming", height: 180}
	var person1= Person("xaiownag",175);
	console.log(person1)					//Object {name: "xaiownag", height: 175}
</script>
************************************
*                                  *
*      new关键字、构造函数三段论     *
*                                  *
************************************
在JavaScript中，使用new关键字后，意味着做了如下四件事情：
【1】创建一个新的对象(可以理解为生成this空对象)，这个对象的类型是object;
【2】设置这个新的对象的内部、可访问性和[[prototype]]属性为构造函数（指prototype.construtor所指向的构造函数）中所设置的形式;
【3】执行构造函数，当this关键字被提及的时候，使用新创建的对象的属性(空对象this内填入写入值);
【4】返回新创建的对象（除非构造方法中指定了返回的对象）
出现new时系统会隐式生成 var this ={};和 return this; 例：
<script type="text/javascript">
	function Person(){
		//var this = {};
		this.name = 'abc';
		this.age = 123;
		//return this;
	}
	var person = new Person();	//执行这步时才有两个隐式this语句
	Person();					//直接执行Person()则没有隐式this语句
</script>
在创建新对象成功之后，如果调用一个新对象没有的属性的时候，JavaScript会沿着原型链向止逐层查找对应的内容,这类似于传统的'类继承'。
注意：在第二点中所说的有关[[prototype]]的属性，只有在一个对象被创建的时候才起作用；
	 比如使用new关键字、使用Object.create、基于字面意义的改写（函数默认为Function.prototype，数字默认为Number.prototype等）
	 它只能被Object.getPrototypeOf(someObject)所读取,没有其他任何方式来设置或读取这个值。
举例说明:
<script type="text/javascript">
	ObjMaker = function() {this.a = 'first';};	//ObjMaker只是一个用于作为构造器的方法，没有其他意义。
	ObjMaker.prototype.b = 'second';
												//与其他函数类似,ObjMaker拥有一个可被我们修改的prototype属性. 我们添加一个属性b给它
												//与所有对象一样,ObjMaker也拥有一个不可访问的[[prototype]]属性，我们无法对其进行改变

	obj1 = new ObjMaker();					
	//控制台输入obj1.a;返回'first'
	//控制台输入obj1.b;返回'second'
	//obj1没有'b'属性，所以JavaScript在它的[[prototype]]中查找
	//它的[[prototype]]与ObjMaker.prototype属性一致
	//而ObjMaker.prototype属性有一个叫'b'的属性，其值为'second'，所以返回'second'
</script>
※※※这里发生了三件事情：
【1】一个叫obj1的空对象被创建，首先obj1与{}一致；
【2】obj1的[[prototype]]属性被设置为ObjMaker的原型属性的拷贝；
【3】ObjMaker方法被执行，所以obj1.a被设置为'first'。
模仿继承:
可以使用如下的方式实例化ObjMaker类的子类：
<script type="text/javascript">
	ObjMaker = function() {this.a = 'first';};
									//ObjMaker只是一个用于作为构造器的方法，没有其他意义。
	ObjMaker.prototype.b = 'second';
									//与其他函数类似,ObjMaker拥有一个可被我们修改的prototype属性. 我们添加一个属性b给它
									//与所有对象一样,ObjMaker也拥有一个不可访问的[[prototype]]属性，我们无法对其进行改变
	SubObjMaker = function () {};
	SubObjMaker.prototype = new ObjMaker(); //由于这里使用了new关键字，所以SubObjMaker的[[prototype]]属性被设置为ObjMaker.prototype的一个拷贝
	SubObjMaker.prototype.c = 'third';
	obj2 = new SubObjMaker();				//obj2的[[prototype]]属性被设置为SubObjMaker的prototype属性的一个拷贝
	
	//控制台输入obj2.c;返回'third',来自SubObjMaker.prototype
	//控制台输入obj2.b;返回'second',来自ObjMaker.prototype
	//控制台输入obj2.a;返回'first',来自ObjMaker构造函数内的{this.a = 'first';};
	//这是因为SubObjMaker是使用ObjMaker的构造方法创建的,obj2.a会通过原型链一路向上找到的ObjMaker构造函数里的this.a属性,从而返回'first'。
</script>
<script type="text/javascript">
    function Person () {
    	this.sex = 'abc';
    	this.eat = [1,2];
    }   										
    Person.prototype = {					//直接给Person的原型指定对象字面量。则这个对象的constructor属性不再指向Person函数
        name:"志玲",
        age:20
    };
    var p1 = new Person(),
    	p2 = new Person();					//此时由于p1和p2都是被function Person (){...}刚刚构造出的对象,所以在各个属性的数值上都完全相等,但却代表着两个完全不同的地址

    console.log(p1.sex === p2.sex);			//打印true, 	原始值比较的是值本身
	console.log(p1.eat === p2.eat);			//打印false,引用值比较的是引用地址

    p1.sex = '123';
    console.log(p1.sex);					//打印123
    console.log(p2.sex);					//打印abc
    p1.eat.push('3');
    console.log(p1.eat);					//打印[1, 2, "3"]
    console.log(p2.eat);					//打印[1, 2]
</script>
使用new关键字实例化新对象时可以往里面传参数,而且可以传多个
例:
<script type="text/javascript">
	function test(c,d){
		var a = 123;
		function b(){};
		this.c = c;
		this.d = d;
	}
	var e = new test(1);
	var f = new test(2,3,4);
	console.log(e);							//打印 	test {c: 1, d: undefined}
	console.log(f);							//打印 	test {c: 2, d: 3}
</script>
在执行new 构造函数操作后,this就是构造函数的一个实例了,如果没有new关键字,直接执行函数体,则this不是函数体的实例
例:
<script type="text/javascript">
	function Foo(){
		if(this instanceof Foo){
			console.log('yes');
		}
		else{
			console.log('no');
		}
	};
	new Foo();								//打印 	yes
	Foo();									//打印 	no
</script>
一、使用new关键字实例化的时候发生了什么？
以Test()函数举个栗子：
【1】第一步，创建一个空对象：var test={};
【2】第二步，将构造函数Test()中的this指向新创建的对象test。
【3】第三步，将test的_proto_属性指向Test函数的prototype，创建对象和原型间关系
【4】第四步，执行构造函数Test()内的代码。
二、构造函数有return值怎么办？
	构造函数里没有显式调用return时，默认是返回this对象，也就是新创建的实例对象。
	当构造函数里调用return时，分两种情况：
【1】return的是五种简单数据类型：String，Number，Boolean，Null，Undefined；
	这种情况下，忽视return值，依然返回this对象。
【2】return的是Object；
	这种情况下，不再返回this对象，而是返回return语句的返回值。
<script type="text/javascript">
	function Person(name){
        this.name = name;
        return {name:"cherry"}
    }
    var person = new Person("sheila");
    person.name;	//cherry
    person;			//Object {name: "cherry"}
</script>
**************************
*                        *
*        原型、原型链     *
*                        *
**************************
https://blog.csdn.net/u012468376/article/details/53121081
https://blog.csdn.net/u012468376/article/details/53127929
原型
定义：原型是function对象(也就是函数)的一个属性，它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象，可以继承该原型的属性和方法，原型也是对象。
原型的意义就是可以让构造函数内的代码实现低耦合，把被构造的对象的共有属性全放入原型内，把被构造对象的私有属性写在构造函数内，这样能降低代码耦合度。
例：
<script type="text/javascript">
	//Person.prototype = {}		原型是祖先，在函数刚刚出生时就被定义好了，刚开始是空对象
	Person.prototype.LastName = "Deng";
	function Person(){
	}
	var person = new Person();
	var person1 = new Person();

	console.log(person.LastName);	//输出Deng,虽然function Person(){}里是空，但他会上Person.prototype里去找LastName属性(继承)
	console.log(person1.LastName);	//输出Deng
</script>
但无法通过改变子元素的属性顺着原型链,来改变原型链元素上的属性:
<script type="text/javascript">
	//Grand.prototype.__proto__ = Object.prototype  此值为系统默认,Object.prototype(也就是Object {})是所有对象的最终原型
	Grand.prototype.lastName = "Deng";
	function Grand(){
	}
	var grand = new Grand();

	Father.prototype = grand;
	function Father(){
		this.name = 'xuming';
		this.num = 100;
		this.fortune = {
			card1 : 'visa'
		}
	}
	var father = new Father();

	Son.prototype = father;
	function Son(){
		this.hobbit = "smoke";
	}
	var son = new Son();
	//控制台输入son.lastName; 会输出"Deng"
	//控制台输入son.num ++; 会输出100，
	//控制台再输入son.num ++; 会输出101，
	//控制台输入father.num; 还是会输出100
	//控制台输入son.fortune = 200
	//此时输入son会输出Son{hobbit:"smoke", fortune: 200}
	//father的fortune属性不会改变
	//控制台输入son.fortune.card2 = 'master'
	//father.fortune会变成{card1 : 'visa',card2 : 'master'}
</script>
*******************************************************
var obj1 = {} 与 var obj1 = new Object() 是完全等价的
obj1的原型(obj1.__proto__)就是Object.prototype(这是所有对象的最终原型)

一种更加灵活的创建原型的方法：var obj = Object.create(原型)
var obj = Object.create(原型)的使用方法：
例1：
<script type="text/javascript">
	var obj ={name : "sunny", age : 123};
	var obj1 = Object.create(obj);					//控制台输入obj1.name会输出sunny
</script>
例2：
<script type="text/javascript">
	Person.prototype.name = "sunny";
	function Person(){
		this.num = 100;
	}
	var person = Object.create(Person.prototype);	
	//此时person的原型就和构造工厂里Person()的原型一样了，
	//输入person.name会输出"sunny"
	//但Person里自己构造的自定义的东西person是无法继承的。注意现在person继承的只是Person原型(Person.prototype)里的属性和方法
	//而Person里自己的自有属性(比如this.num = 100;)是无法继承的
	//输入person.num会输出undefined
</script>
********************************************************
常见问题:
【1】绝大多数对象最终都会继承自Object.protoype,这里为什么要强调是绝大多数?
	因为Object.create(原型)的括号里可以填null,而且在使用Object.create(原型)方法创建对象时,
	创建后的对象的.__proto__属性再人为添加或修改时就不行了,即使再添加也不会被系统当做原型属性了
例：
<script type="text/javascript">
	var obj1 = Object.create(null);
	obj1.__proto__ = {name : "sunny"}	//控制台输入obj1.name会返回undefined
</script>
【2】为什么undefined和null没有toString()方法?
	因为toString()方法是放在Object{}内的，而undefined和null是没有原型的所以也就无法访问到Object {}
<script type="text/javascript">
	var obj = {};
	document.write(obj)					//输出[object Object]  其实是系统在调用obj.toString
</script>
*************************************
*                                  	*
*     prototype和__proto__的区别   	*
*                                  	*
*************************************
【一】prototype和__proto__的概念
prototype是函数的一个属性（每个函数都有一个prototype属性）,这个属性是一个指针，指向一个对象，它是显示修改对象的原型的属性。
__proto__是一个对象拥有的内置属性（请注意：prototype是函数的内置属性，__proto__是对象的内置属性），是JS内部使用寻找原型链的属性。
用chrome和FireFox都可以访问到对象的__proto__属性，IE不可以。
【二】只有当对象的.__proto__属性是系统内部默认的属性时,系统才会把他当做是系统真正意义上的原型属性,而人为添加.__proto__属性系统不承认,但可以人为更改(人为添加不可以,但人为更改可以)。
所以在使用Object.create(null)方法创建对象时,原型是null(空对象),创建后如果人为自己添加.__proto__属性系统就不承认了,就失去了作为原型的属性特有性质,但可以访问。
而在使用其他方法创建对象时,其实系统内部已经给他自动隐式添加了.__proto__属性,所以就不会出现这种状况。
注意:在JavaScript里,创建的任何对象(除非是以Object.create(null)方法创建的对象)系统内部都会自动隐式添加.__proto__属性,
所以其实在给以Object.create(null)方式创建的对象，人为添加的.__proto__属性的时候
在这个人为添加的.__proto__属性值内部又被自动隐式添加了.__proto__属性(因为对象的.__proto__属性值也是一个对象)
<script type="text/javascript">
	var obj1 = Object.create(null);
	obj1.__proto__ = {name : "one"};		//对象的.__proto__属性不可以人为添加
	console.log(obj1.__proto__);			//打印 	{name: "one"}
	console.log(obj1.name);					//打印   undefined
	console.log(obj1);						//打印  	{} 	,点开后__proto__ : Object 是深紫色字表示已经被人为改动过

	var obj2 = new Object();
	obj2.__proto__ = {name : "two"};		//对象的.__proto__属性可以人为更改
	console.log(obj2.__proto__);			//打印 	{name: "two"}
	console.log(obj2.name);					//打印 	two
	console.log(obj2);						//打印  	{}	,点开后__proto__ : Object 是浅紫色字表示是系统默认值

	function Obj(){} 
	var obj3 = new Obj();
	obj3.__proto__ = {name : 'three'};		//对象的.__proto__属性可以人为更改
	console.log(obj3.__proto__);			//打印 	{name: "three"}
	console.log(obj3.name);					//打印 	three
	console.log(obj3);						//打印  	Obj {},点开后__proto__ : Object 是浅紫色字表示是系统默认值

	var obj4 = {
		__proto__ :{name : 'four'}			//对象的.__proto__属性可以人为更改
	}
	console.log(obj4.__proto__);			//打印 	{name: "four"}
	console.log(obj4.name);					//打印 	four
	console.log(obj4);						//打印  	{}	,点开后__proto__ : Object 是浅紫色字表示是系统默认值
	obj4.__proto__ = {name : 'five'};		//对象的.__proto__属性可以人为更改
	console.log(obj4.__proto__);			//打印 	{name: "five"}
	console.log(obj4.name);					//打印 	five
	console.log(obj4);						//打印  	{}	,点开后__proto__ : Object 是浅紫色字表示是系统默认值
</script>
【三】new 的过程
以<script type="text/javascript">
	var Person = function(){};
	var p = new Person();
</script>
为例：
new的过程拆分成以下三步：
【1】 var p={}; 也就是说，初始化一个对象p
【2】 p.__proto__ = Person.prototype;
【3】 Person.call(p); 也就是说构造p，也可以称之为初始化p

关键在于第二步，我们来证明一下：
<script type="text/javascript">
	var Person = function(){};
	var p = new Person();
	console.log(p.__proto__ === Person.prototype);		//打印 true; 说明我们步骤2是正确的。
</script>
【四】在寻找实例化后的对象的属性或方法时,会沿着原型链逐渐向上找:
<script type="text/javascript">
	var Person = function(){};
    Person.prototype.sayName = function() {
        console.log("My Name is Jacky");	
    };
    Person.prototype.age = 27;
    var p = new Person();
    p.sayName();						//打印 My Name is Jacky
</script>
p是一个引用指向Person的对象,我们在Person的原型上定义了一个sayName方法和age属性，当我们执行p.age时，会先在this的内部查找（也就是构造函数内部），
如果没有找到然后再沿着原型链向上追溯。
这里的向上追溯是怎么向上的呢？这里就要使用__proto__属性来链接到原型（也就是Person.prototype）进行查找,最终在原型上找到了age属性。
【五】可以通过改变实例化后的对象的.__proto__属性,改变构造函数的.prototype属性：
<script type="text/javascript">
	function Person(){
	}
	Person.prototype.age = 123;
	var me  = new Person();
	var her = new Person();
	me.__proto__.age = 111;
	console.log(Person.prototype.age);	//打印 111; prototype与__proto__引用的是地址
	console.log(her.__proto__.age);		//打印 111; prototype与__proto__引用的是地址
</script>
*****************************************************************************************
例：
<script type="text/javascript">
	Person.prototype.name = 'abc';
	function Person(){
		//var this = {
		//	...
		//	__proto__ : Person.prototype
		//}
	}
	var obj = {
		name : "sunny"
	}
	var person = new Person();
	person.__proto__ = obj;			
	//__proto__属性只能由被构造的对象person调用并且可以更改对象里的内容(person.__proto__ = {...})
	//prototype属性只能由构造函数Person调用(Person.prototype),
	//调用后可以赋值更改其属性比如Person.prototype.name = 'sunny';
	//也可以赋给引用值(比如对象)，比如Person.prototype = {name : 'cherry'}，
	//但想要赋给引用值的话,必须要在出现构造函数的标志new前赋值完毕，在此语句后赋给的引用值将不会被执行
	//此时控制台输入person.name会输出"sunny"
</script>
												********************
												*上下两个例子要结合看*
												********************
<script type="text/javascript">
	Person.prototype.name = 'sunny';
	function Person(){
		//var this = {__proto__: Person.prototype}	//此时声明了this对象,__proto__属性指向Person.prototype所指向的对象空间(空对象)
	}
	var person = new Person();	...............     //构造函数标志，说明Person()是构造函数

	Person.prototype = {							//此时Person.prototype重新指向了新的对象空间{name : 'cherry'}
		name : 'cherry'								//但因为Person.prototype = {name : 'cherry'}是在构造函数标志new后出现的
	}												//导致this对象里的__proto__属性已经生成了，没法再改了，除非再单独修改单个属性值了
													//比如如果修改Person.prototype.name是可以的
													//所以Person.prototype = {name : 'cherry'}这句并不会改变上面__proto__所指向的对象空间	
													//此时控制台输入person.name会输出'sunny'
	//相当于：
	// var obj = {name : "sunny"};					//Person.prototype = {name : "sunny"};
	// var obj1 = obj;								//__proto__ = Person.prototype;---------> __proto__属性才是对象所需要的原型属性
	// obj = {name : "cherry"};						//Person.prototype = {name : 'cherry'}--> Person.prototype改变了但是__proto__没有改变
</script>
												********************
												*上下两个例子要结合看*
												********************
但下面就不一样了：
<script type="text/javascript">
	Person.prototype.name = 'sunny'
	function Person(){}
	Person.prototype = {						//此引用值赋值语句是在出现构造函标志new前赋值的，所以将覆盖原来的Person.prototype属性
		name : 'Cherry'
	}
	var person = new Person();	...............	//构造函数标志
												//此时控制台输入person.name会输出'Cherry'
</script>
												********************
												*上下两个例子要结合看*
												********************
<script type="text/javascript">
	function Person(){}
	var person = new Person();	............	//构造函数标志
	Person.prototype.name = 'cherry'			//此时控制台输入person.name会输出'Cherry'
</script>
												**************************
												* console.log与控制台输入 *
												**************************
<script type="text/javascript">
	console.log(a);			//undefined
	var a;
	console.log(a);			//undefiend
	a = 1;
	console.log(a);			//1
	a = 2;
	console.log(a);			//2
	a = 3;					//控制台中输入a  	将会输出3
</script>
*****************************************************************************
*                                                                         	*
*  给原型对象赋值时,Person.prototype.name、person.__proto__.name、         	*
*  Person.prototype、person.__proto__与new标志的位置关系对对象原型属性的影响  	*
*                                                                         	*
*****************************************************************************
以
<script type="text/javascript">
	function Person(){}
	var person = new Person();
</script>
为例：
【1】Person.prototype和person.__proto__与new标志的位置关系对对象原型属性的影响:
只要是给Person.prototype或person.__proto__赋值，就一定是给这两个对象赋引用值(因为只能给这俩属性赋值为对象类型的值)。
【1.1】Person.prototype:
由于在出现构造函标志new时，系统会将Person.prototype的值赋给person.__proto__，
这样的话Person.prototype赋值语句只有出现在构造函标志new前赋值才能被赋值给person.__proto__。
一旦出现new标志，系统将会引用这个原型对象(就是将Person.prototype的值赋给person.__proto__),
所以在new标志后出现的Person.prototype赋值将会被认为是一个全新的变量,与person.__proto__就没有任何关系了，
所以Person.prototype必须在new标志出现前的赋值才会对对象原型的属性有影响。
例：
<script type="text/javascript">
	var Person = function(){};   
    var p = new Person();		
    Person.prototype = {		//Person.prototype出现在了new标志之后，里面的语句和person.__proto__没有任何关系了
    	name : "123",
    }							//控制台输入p.name会输出"undefined"
</script>

<script type="text/javascript">
	var Person = function(){};     
    Person.prototype = {		//Person.prototype出现在了new标志之前，里面的语句会赋值给person.__proto__
    	name : "123",
    };
    var p = new Person();		//控制台输入p.name会输出"123"
</script>
【1.2】person.__proto__:
由于在出现构造函标志new时，系统才会将Person.prototype的值赋给person.__proto__，所以，在出现new标志前系统是没有person.__proto__属性的,
给他赋值会报错(Cannot set property '__proto__' of undefined),在出现new标志后对其更改才有意义，
所以person.__proto__必须在new标志出现后的赋值才会对对象原型的属性有影响。
例：
<script type="text/javascript">
	var Person = function(){};  
    p.__proto__ = {
    	name : "123"
    }
    var p = new Person();		//p.__proto__出现在new标志之前会报错(Cannot set property '__proto__' of undefined)
</script>

<script type="text/javascript">
	var Person = function(){};   
    var p = new Person();
    p.__proto__ = {
    	name : "123"
    }							//控制台输入p.name会输出"123"
</script>
【2】Person.prototype.name和person.__proto__.name与new标志的位置关系对对象原型属性的影响:
【2.1】person.__proto__.name
由于在出现构造函标志new时，系统才会将Person.prototype的值赋给person.__proto__，所以，在出现new标志前系统是没有person.__proto__属性的，
也就没有person.__proto__.name，给他赋值会报错(Cannot set property '__proto__' of undefined),在出现new标志后对其更改才有意义，
所以person.__proto__.name必须在new标志出现后的赋值才会对对象原型的属性有影响。(这一点和1.2person.__proto__很类似)
例：
<script type="text/javascript">
	var Person = function(){};     
    p.__proto__.name = '456';
    var p = new Person();		//p.__proto__.name出现在new标志之前会报错(Cannot read property '__proto__' of undefined)
</script>

<script type="text/javascript">
	var Person = function(){};
    var p = new Person();
    p.__proto__.name = '456';	//控制台输入p.name会输出"456"
</script>
【2.2】Person.prototype.name:
在出现构造函标志new时，系统会将Person.prototype的值赋给person.__proto__，
也就是说在出现new关键字时person.__proto__与Person.prototype指向同一个地址(因为原型对象是引用值)
此时在原型对象Person.prototype里添加了一个新的键值对Person.prototype.name,则此时person.__proto__指向的对象也会发生改变
所以Person.prototype.name(在原型对象里添加新的键值对)与new标志的位置无关，且位置靠后的赋值语句会覆盖靠前的语句。
<script type="text/javascript">
	function Person(){}
	var person = new Person();	............	//构造函数标志
	Person.prototype.name = 'cherry'			//此时控制台输入person会输出'__proto__: {constructor: Person(), name: "cherry"}'
</script>
※※※【3】强制给person.__proto__属性赋值对对象原型属性的影响：
虽然Person.prototype.name赋值语句对对象原型属性的影响与new标志的位置无关，
但是如果在new标志之后又出现了person.__proto__赋值语句(强制给person.__proto__属性赋值)，
则person.__proto__属性将会指向这个全新的对象，对象里有啥算啥没有的就是undefined，
在这条强制赋值语句后就算赋值Person.prototype.name(或age、sex等等属性)也没有用了，因为person.__proto__已经不指向Person.prototype对象了，
换句话说，一旦出现person.__proto__赋值语句不管在他之前还是之后出现的Person.prototype.name(或age、sex等等属性)和Person.prototype赋值语句，通通被他覆盖，
但是，在这条强制赋值语句后再追加person.__proto__.name(或age、sex等等属性)是可以的。
例:
<script type="text/javascript">	
	var Person = function(){};
	Person.prototype = {				
     	name : "a",
    };
    var p = new Person();     
    p.__proto__.name = 'b';    
    Person.prototype.name = 'c';
    p.__proto__ = {
    	name : "d"
    }
    p.__proto__.name = 'e';
    Person.prototype.name = 'f';	//控制台输入p.name会输出"e",
    								//由于p.__proto__ = {name : "d"}的存在导致Person.prototype.name = 'f'赋值无效
    								//如果没有p.__proto__ = {name : "d"}这一句，则会输出'f'
</script>
另：
Person.prototype、person.__proto__、Person.prototype.name与person.__proto__.name、一定要出现在var Person = function(){};语句之后，
否则会报错(Cannot read property 'prototype' of undefined)，
因为只有在出现var Person = function(){}语句之后，Person才被赋值为function(){}，只有这样Person才是一个函数，才具有prototype属性；
person.__proto__和person.__proto__.name就更不用说了，需要在出现构造函标志new后才行。
例：
<script type="text/javascript">
	Person.prototype = {				//报错(Cannot set property '__proto__' of undefined)
     	name : "123",
     };
    Person.prototype.name = "abc";		//报错(Cannot set property '__proto__' of undefined)
	var Person = function(){}			//执行到这句Person才具有函数的属性，才有.prototype属性值	
    var p = new Person(); 
</script>
*****************************************************
*                                                   *
*   JavaScript对象、constructor、__proto__之深入剖析 	* 
*                                                   *
*****************************************************
prototype与__proto__的区别：
每创建一个函数，就会有一个prototype属性，这个属性是一个指针，指向一个对象，而这个对象的用途是包含由特定类型或者实例(被构造的对象)共享的属性和方法;
每生成一个对象(包括普通对象和函数对象)，就会有一个__proto__属性,原型链上的对象正是依靠这个__proto__属性连结在一起的,查找属性时会沿着__proto__向上查找。

prototype 和 new：
不是所有的对象都有prototype属性，只有函数对象才有prototype属性（当然也可以给普通对象赋予prototype属性，但是没有什么意义）。
prototype和new关键字息息相关，prototype的作用只有在用new时才能体现出来，其他时候无用。

JavaScript对象：
JavaScript一切都是对象，普通函数是对象，Object、Array、String等内置对象都是对象，所谓对象就是{}
抛开Array、String等内置对象，JavaScript引擎提供了最基础的两种对象（规范里并没有这么写,是自己想的）,
暂且分别称之为 普通对象、函数对象，其实它们的区别也仅仅是创建的时候属性不同而已。
【1】普通对象
普通对象可以用'var obj = {}'或者'var obj = new Object()'两种方式创建，从打印的结果可以发现两种方式生成的普通对象是相同的。
普通空对象只有一个__proto__属性
【2】函数对象
函数对象用function创建，函数对象的name属性就是函数名称，空函数对象有很多属性, Object实际上就是一个空函数对象，只不过现代浏览器为Object对象添加了很多私有方法。
【3】下面测试一下普通对象、函数对象、和内置的Object的__proto__属性：
<script type="text/javascript">
	var a = {};
	function b(){};
	b.__proto__ === Object.__proto__ === function () { [native code] }  
	//空函数对象的__proto__属性和内置Object的__proto__是相同的,证明内置Object实际上就是一个空函数对象,一切函数的__proto__都是function () { [native code] } 
	a.__proto__ === Object.__proto__.__proto__ === {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}			
	//普通空对象的__proto__和空函数对象（Object）__proto__的__proto__是同一个,都是Object {}
	a.__proto__.__proto__ === null             							
	//JavaScript中所有对象的顶层__proto__属性都是null
</script>
【3.1】Object函数
Object函数是一个构造函数,可以通过new Object()方法创建对象
【3.2】Object.__proto__对象
Object.__proto__对象是系统内置的function () { [native code] }; 其实几乎和空函数没什么区别, 
唯一的区别就是b.name === "b"而Object.__proto__.name === "", 因为没有函数名字，因此new关键字是创造不出来这个对象的，只能JS引擎内置。
Object.__proto__ === function () { [native code] }
【3.3】Object.__proto__.__proto__对象
这也是一个JS引擎内置的对象，
它的属性见普通空对象的__proto__属性，它的__proto__属性(Object.__proto__.__proto__.__proto__)为null,且a.__proto__.__proto__ === null 

【4】JavaScript里所有类型的测试：
<script type="text/javascript">
						//******************************************         控制台        ********************************************//

	var a = 1,			//typeof a           	-->"number"	 	a.__proto__                     -->Number {[[PrimitiveValue]]: 0}				这是一个由Number型数据专有属性和方法构成的对象	
						//a.__proto__.__proto__ -->Object {}	a.__proto__.__proto__.__proto__ -->null
						//a.constructor         -->function Number() { [native code] }	这是一个函数,Number型数据的构造函数是一个函数
						
		b = "abc",		//typeof b           	-->"string"		b.__proto__                     -->String {length: 0, [[PrimitiveValue]]: ""}	这是一个由Srting型数据专有属性和方法构成的对象
						//b.__proto__.__proto__ -->Object {}	b.__proto__.__proto__.__proto__ -->null
						//b.constructor         -->function String() { [native code] }	这是一个函数,String型数据的构造函数是一个函数
						
		c = true,		//typeof c           	-->"boolean"	c.__proto__       		        -->Boolean {[[PrimitiveValue]]: false}			这是一个由Boolean型数据专有属性和方法构成的对象
						//c.__proto__.__proto__ -->Object {}	c.__proto__.__proto__.__proto__ -->null
						//c.constructor         -->function Boolean() { [native code] }	这是一个函数,Boolean型数据的构造函数是一个函数
						
		d = [],			//typeof d           	-->"object"	 	
		                //d.__proto__           -->[constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, shift: ƒ, …]	这是一个数组型的对象,里面包含了数组类型数据的专有属性和方法
						//d.__proto__.__proto__ -->Object {}	d.__proto__.__proto__.__proto__ -->null
						//d.constructor         -->function Array() { [native code] }	这是一个函数,数组型数据的构造函数是一个函数
						
		e = {},			//typeof e           	-->"object"	 	e.__proto__       		        -->Object {}	这是一个对象
						//e.__proto__.__proto__ -->null
						//e.constructor         -->function Object() { [native code] }	这是一个函数,对象型数据的构造函数是一个函数
						
		f = null,		//typeof f           	-->"object"	 	f.__proto__      		        -->报错(Cannot read property '__proto__' of null)
						//f.constructor         -->报错(Cannot read property 'constructor' of null)
						
		g;				//typeof g           	-->"undefined"	g.__proto__      		        -->报错(Cannot read property '__proto__' of undefined)
						//g.constructor         -->报错(Cannot read property 'constructor' of undefined)

	function h(){		//typeof h           	-->"function"  	h.__proto__ ->function () { [native code] }		这是一个由函数型数据专有属性和方法构成的对象					
		this.name = 'k'	//h.__proto__.__proto__ -->Object {}	h.__proto__.__proto__.__proto__ -->null
	};					//h.constructor         -->function Function() { [native code] }这是一个函数,函数的构造函数是一个函数
						
						//Object                -->function Object() { [native code] }	这是一个函数,Object其实就是一个函数名为Object的函数
						//typeof Object     	-->"function" ,再次证明Object就是一个函数

						//Object.prototype      -->{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}	这就是Object {}
						//Object.__proto__      -->function () { [native code] }		这是一个对象,是一个由函数型数据专有属性和方法构成的对象
						//Object.constructor    -->function Function() { [native code] }这是一个函数,函数的构造函数是一个函数

						//Object.__proto__.__proto__ -->{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}这就是Object {}        
						//Object.__proto__.__proto__.__proto__                                  -->null
									    
						//Object.__proto__.__proto__.constructor                   				-->function Object() { [native code] } === Object  			这就说明Object {}的构造函数是Object
						//注意：(任意函数名).__proto__.__proto__ === (任意函数名).prototype.__proto__ === Object.prototype === Object {}
</script>
*******************************
*                             *
*         call/apply          *
*                             *
*******************************
call/apply:
【1】作用，改变this指向;
【2】区别，后面传的参数形式不同。
call和apply都是方法(函数)的一个属性，任何方法都可以使用call属性
call 需要把实参按照形参书写的格式逐个传进去
apply需要传一个arguments(实参列表)
在 function test(){}里
控制台输入test(),实际上是内部执行了test.call()
例:
<script type="text/javascript">
	function Person(name, age){
		//this == obj
		this.name = name;
		this.age = age;
	}
	var person = new Person('deng', 100);	//不出现new的话，this默认指向window
	var obj = {}
	Person.call(obj, 'cheng', 300);			//这一步会让Person里面所有预设的this全部变成obj,此时控制台输入obj 会输出Object {name:"cheng",age:300}
</script>
不出现new的话,this默认指向window,例:
<script type="text/javascript">
	var name = 123;
	function Person(name, age){
		this.name = name;
		this.age = age;
	}
	Person('Lee', '25');
	console.log(name);						//打印 	Lee
	console.log(age);						//打印 	25
</script>
***************************************************

如果想构建新的函数Student,而且Student里面又完全包含了Person函数的属性,则可以通过调用函数的call属性来简洁代码：
<script type="text/javascript">
	function Person(name, age, sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	function Student (name, age, sex, tel, grade){
		Person.call(this, name, age, sex);		
			//相当于写了this.name = name;
			//		   this.age = age;
			//         this.sex = sex;
			//使用apply方法：
			//Person.apply(this,[name,age,sex])
		this.tel = tel;
		this.grade = grade;
	}
	var student = new Student('sunny', 123, 'male', 138, 2017);
</script>
<script type="text/javascript">
	function Point(x, y){
		this.x = x;
		this.y = y;
		console.log(this.x + ',' + this.y);
		this.moveTo = function(x, y){
			this.x = x;
			this.y = y;
			console.log(this.x + ',' + this.y);
		}
	}
	var p1 = new Point(1, 1);
	var p2 = {x:0, y:0};   
	p1.moveTo(2, 2); 				
	p1.moveTo.apply(p2, [10, 10]);	
	//先打印1,1 	后打印2,2 最后打印10,10   	  apply函数里的p2只改变moveTo函数里的this指向,不改变Point构造函数里的this指向	
	//此时控制台输入p2会输出{x: 10, y: 10}; --> 因为p2是引用值,p2在调用moveTo函数时改变了this.x和this.y,所以p2里的值也改变了
</script>
*****************************
*                           *
*           继承           	*
*                           *
*****************************
例(Son函数原型继承Father函数原型)：
<script type="text/javascript">
	Father.prototype.lastname = "li";
	function Father(){}
	function Son(){}
	function inherit(Target, Origin){
		Target.prototype = Origin.prototype
	}
	inherit(Son,Father);
	var son = new Son;
</script>
但此种方法有一个问题：
<script type="text/javascript">
	Father.prototype.lastName = 'Li';
	function Father(){}
	function Son(){}
	function inherit(Target, Origin){
		Target.prototype = Origin.prototype;//从现在开始往后Target.prototype与Origin.prototype对象会引用同一个地址下的对象
	}
	inherit(Son, Father);					//执行inherit函数,执行后Son.prototype与Father.prototype指向相同地址的对象
	Son.prototype.age = "111";				//此时只想改变Son原型的age属性，但因为共用了原型所以导致Father.prototype.age也变成了111
	console.log(Father.prototype.age);		//打印 111
	var son = new Son();					//从现在开始,由于son.__proto__与Son.prototype、Father.prototype指向同一个地址下的对象
	Son.prototype.age = "222";
	console.log(Father.prototype.age);		//打印 222
	son.__proto__.age = "333";			
	console.log(Father.prototype.age);		//打印 333
	var father = new Father(); 
	Son.prototype.age = "333";
	console.log(Father.prototype.age);		//打印 333
	console.log(father.__proto__.age);		//打印 333
	son.__proto__.age = "444";
	console.log(Father.prototype.age);		//打印 444
	console.log(father.__proto__.age);		//打印 444
</script>
*************************
*                       *
*      圣杯模式继承      *
*                       *
*************************
 ___________________________________________________________________
|	                    Father.prototype                          	|
|———————————————————————————————————————————————————————————————————|
|Father	                        |Son                                |
|	    						|function F(){}                  	|
|								|F.prototype = Father.prototype  	|
|								|Son.prototype = new F() 		 	|
|_______________________________|___________________________________|
例：
<script type="text/javascript">
	Father.prototype.lastname = "Li";
	function Father(){}
	function Son(){}
	function inherit(Target, Origin){
		function F(){};								//先声明F是一个函数，让F有prototype属性
		F.prototype = Origin.prototype;
		Target.prototype = new F();					//这是让Target.prototype变成由工厂函数F构造的一个对象，
													//所以，此时Target.prototype.__proto__ = F.prototype = Origin.prototype
													//并且可以保证更改Target.prototype(Son.prototype)里的属性，
													//而使Origin.prototype(Father.prototype)里的属性不受影响
													
		Target.prototype.constructor = Target;		//给由Target工厂函数产出的对象的constructor属性归位，归位成自己
													//如果不归位的话,打印son.constructor会发生:
													//此时son.__proto__(也就是Son.prototype)是由工厂函数F构造的一个对象new F();
													//这个对象的__proto__属性(new F().__proto__)就是Father.prototype
													//所以son一路向上找自己的构造函数constructor，就找到了Father，注意，new F()是对象
													//而constructor找的是函数，所以一路向上找到了Father(){}
													//所以此时输入son.constructor会输出function Father(){ };
		Target.prototype.uber = Origin.prototype;	//给自己编一个属性，通过这个属性可以找到自己的超类(Target究竟继承自谁)
	}
	inherit(Son, Father)
	var son    = new Son();
	var father = new Father();												
</script>
*****************************
*                           *
*        组合函数继承        *
*                           *
*****************************
组合函数利用了原型继承和构造函数借调继承的优点，组合在一起,成为了使用最广泛的一种继承方式
<script type="text/javascript">	
    function Father (name, age) {	//定义父类型的构造函数
        							//属性放在构造函数的自有属性中,因为实例化后的不同实例对象可能会有不同的属性值
        this.name = name;
        this.age = age;
        													//方法定义在原型中,因为实例化后的不同实例对象也往往会使用相同的方法
        if((typeof Father.prototype.eat) != "function"){	//判断.eat这个属性是不是function,如果不是function则证明是第一次创建对象，
            Father.prototype.eat = function () {			//则把这个funcion添加到原型中。
                alert(this.name + " 在吃东西");				//如果是function，则代表原型中已经有了这个方法，则不需要再添加。
            }
        }  
    }							
    function Son(name, age, sex){	//定义子类类型的构造函数
        							//借调父类型的构造函数，相当于把父类型中的属性添加到了未来的子类型的对象中
        Father.call(this, name, age);
        this.sex = sex;
    }							
    Son.prototype = new Father();	//修改子类型的原型为父元素实例化后的对象,这样子类型(实例化后的对象)就可以沿着原型链向上找,从而继承父类型中的方法了。
    var son1 = new Son("志玲", 30, "女");
    alert(son1.name);
    alert(son1.sex);
    alert(son1.age);
    son1.eat();
</script>
说明：
组合继承是我们实际使用中最常用的一种继承方式,但是有一个问题:Son.prototype = new Father();
这不照样把父类型的属性给放在子类型的原型中了吗，不应该还是会有共享问题吗？
答:我们是在子类型的构造函数中借调的父类型的构造函数,也就是说,
子类型的原型中有的属性,都会被子类对象中的属性给覆盖掉,除非没有的话才会向上找(按照原型链查找到顺序,会先查找子类型的原型然后再去找父类型元素的原型)
********************************************************
雅虎公司给出的继承函数：
<script type="text/javascript">
	var inherit = (function(){
		var F = function(){};
		return function (Target, Origin){			//形成闭包，使F = function(){}成为function (Target,Origin)函数体内的私有变量
													//由于F本身并没有什么用只是拿来用作过度，所以这样处理非常好
			F.prototype = Origin.prototype;
			Target.prototype = new F();
			Target.prototype.constructor = Target;
			Target.prototype.uber = Origin.prototype;
		}
	}());											//立即执行函数
</script>
*****************************
*                           *
*  方法的连续调用、属性拼接  	*
*                           *
*****************************
实现方法的连续调用可以在方法调用后加return this:
<script type="text/javascript">
	var deng = {
		smoke : function smoke(){
			console.log("smoking ..... cool");
			return this;				//此时this指的是{smoke: ƒ, drink: ƒ, perm: ƒ}(也就是deng{...})
		},
		drink : function drink(){
			console.log("drinking .... cool");
			return this;				//此时this指的是{smoke: ƒ, drink: ƒ, perm: ƒ}(也就是deng{...})
		},
		perm : function perm(){
			console.log("perming .... cool");
			return this;				//此时this指的是{smoke: ƒ, drink: ƒ, perm: ƒ}(也就是deng{...})
		}
	}
	deng.perm().smoke().drink().perm();	//输出 	perming .... cool
										//    	smoking .... cool
										//    	drinking ... cool
										//    	perming .... cool
</script>
*****************************************************
属性拼接：
<script type="text/javascript">
	var deng = {
		wife1 : {name : 'xiaoliu'},
		wife2 : {name : 'xiaowang'},
		wife3 : {name : 'xaiozhang'},
		wife4 : {name : 'xaiochen'},
		sayName : function(num){
			return this['wife' + num];		//相当于return this.wife(num)
		}									//
	}										//var obj = {name : 'abc'};					
											//调用obj.name相当于调用obj['name']
</script>
*********************************
*					  			*	
*   for in循环、hasOwnProperty  	*
*					  			*	
*********************************
for in循环用法：
	for(var 变量 in 对象)
使用for in循环可以遍历对象属性以及对对象进行枚举,注意:for in循环只能遍历枚举对象型数据(比如Object, Array)的属性,其他的数据类型的属性是无法遍历枚举的
使用时通常伴随使用如下方法:
<script type="text/javascript">
	//1
	hasOwnProperty()   	//判断属性名或方法名是不是自己的自有属性(防止遍历到原型中),如果有,返回true,否则返回false
						//在一个对象中,人为在他的原型上添加了新的属性或方法时,该原型链是活动的,这也就意味着所有的对象都会自动获取针对新方法或属性的访问
						//为了避免在枚举时访问该新方法或属性可以调用hasOwnProperty()函数来过滤该原型属性
	//2
	for in               //判断属性名自己能不能访问到,只要能访问到就返回true,否则返回false,会访问原型内的属性
	//3
	A instanceof B    	//判断A对象是不是B构造出来的(原理是看A对象的原型链上有没有B的原型)
						//用法：person instanceof Person
</script>
【1】使用for(var 变量 in 对象)语句,输出属性名：
<script type="text/javascript">
	var obj = {
		name : "123",
		age : 123,
		height : 180,
		weidth : 75
	}
	for (var prop in obj){
		console.log(prop + ' ' + typeof(prop));
	}											
	//会打印 	name string
	//			age string
	//			height string
	//			weidth string
</script>
【2】使用for in循环,输出属性值：
<script type="text/javascript">
	var obj = {
		name : "123",
		age : 123,
		height : 180,
		weidth : 75
	}
	for (var prop in obj){
		console.log(obj[prop] + " " + typeof(obj[prop]));	//注意这里只能使用obj[prop],不能使用obj.prop
	}														//因为如果使用obj.prop,会由系统内部自动转换为obj['prop'],系统会把prop当成obj对象的一个属性
															//而在这里需要prop是一个变量,并不是对象obj的属性													
	//会打印 	123 string
	//			123 number
	//			180 number
	//			75 number
</script>
【3】使用for in循环,让obj1里面所有属性值 + 1：
<script type="text/javascript">
	var num = {
		a : 123,
		b : 456,
		c : 789,
	}
	for(var key in num){
		num[key] ++;				//注意这里key是变量一定要用num[key]的形式，不能用num.key
	}
</script>
【4】for in循环还会打印自己定义的原型属性，如果不想打印自己的原型属性，可以使用对象自带的hasOwnProperty方法：
<script type="text/javascript">
	var obj = {
		name : "123",
		age : 123,
		height : 180,
		weidth : 75,
		__proto__ : {
			lastName : 'Deng'
		}
	}
	for (prop in obj){
		if (obj.hasOwnProperty(prop)){		//obj.hasOwnProperty(prop)可以判断变量prop属性是不是自己的自有属性
											//如果是,返回true,如果不是,返回false(如果不是自有属性就肯定是原型属性,所以返回false)
			console.log(obj[prop]);			//除了__proto__属性,其他的属性都将被打印,会打印:
											//	123
											//	123
											//	180
											//	75
											//这里的obj.__proto__属性是可以人为改变的,此时变为obj.__proto__ : { lastName : 'Deng' },且为浅紫色,所以是原型上的属性,所以不打印
			}
	}
	console.log(obj.__proto__);				//打印 {lastName: "Deng"}
	console.log(obj.lastName);				//打印 Deng
	console.log(obj);						//打印 {name: "123", age: 123, height: 180, weidth: 75},
											//注意:这里不打印obj的__proto__属性,因为人为添加的obj.__proto__属性已变成了obj对象的原型属性
</script>

<script type="text/javascript">
	var obj = {
		name : '123',
		__proto__ : {
			lastName : "Deng"
		}
	}
	Object.prototype.abc = 'aaa'			//Object.prototype是所有对象的终极原型,任何对象通过原型链溯源都可以找到他
	for (var prop in obj){
		if(!obj.hasOwnProperty(prop)){
			console.log(obj[prop]);
		}
	}										//输出  	'Deng',因为自己人为添加了__proto__ : { lastName : "Deng" }
											//		'aaa' ,通过原型链溯源找到的Object.prototype里的Object.prototype.abc = 'aaa'

	var a = {};								//这里随便定义了一个a对象,来验证Object.prototype是所有对象的终极原型,任何对象通过原型链溯源都可以找到他
	for(var prop1 in a){
		if(!a.hasOwnProperty(prop1)){
			console.log(a[prop1]);			//输出 	'aaa' ,通过原型链溯源找到的Object.prototype里的Object.prototype.abc = 'aaa'
		}
	}						
</script>
【5】hasOwnProperty()方法是存在于对象的.__proto__内部的方法,如果对象的.__proto__内部没有此方法直接调用则会报错:
<script type="text/javascript">
	var obj1 = Object.create(null);			//obj1的原型是null,里面啥也没有,当然也没有.__proto__属性,也就没有.hasOwnProperty()方法
	obj1.__proto__ ={ lastName : 'Lee'};	//给obj1里人为添加了.__proto__属性,但这个人为添加的.__proto__属性里依然没有.hasOwnProperty方法(只人为添加了 lastName : 'Lee' 属性)
	for(var prop in obj1){
		if(obj1.hasOwnProperty(prop)){		//报错:Uncaught TypeError: obj1.hasOwnProperty is not a function
			console.log(obj1[prop]);
		}
	}
</script>
********************
*				   *	
*    instanceof    *
*				   *
********************
【1】通常来讲,使用instanceof就是判断一个实例是否属于某种类型。
例(判断 foo 是否是 Foo 类的实例):
<script type="text/javascript">
	function Foo(){} 
	var foo = new Foo(); 
	console.log(foo instanceof Foo)		//打印 	true
</script>
【2】更重的一点是,instanceof可以在继承关系中用来判断一个实例是否属于它的父类型,下面的代码中是判断了一层继承关系中的父类,在多层继承关系中,instanceof运算符同样适用
例(判断 foo 是否是 Foo 类的实例,并且是否是其父类型的实例):
<script type="text/javascript">
	function Aoo(){}; 
	function Foo(){}; 
	Foo.prototype = new Aoo();			//原型继承
	 
	var foo = new Foo(); 
	console.log(foo instanceof Foo)		//打印 	true 
	console.log(foo instanceof Aoo)		//打印 	true
</script>
【3】语言规范中对instanceof运算符的定义:
<script type="text/javascript">
	function instance_of(L, R) {		//L表示左表达式,R表示右表达式
		var obj = R.prototype;			//取R的显示原型
		L = L.__proto__;				//取L的隐式原型
		while (true) { 
			if (L === null) return false;//第一步判断,如果此时的L.__proto__严格等于null,就返回false,如果不等于就不执行任何操作,并且进行第二步判断
			if (obj === L) return true;	//第二步判断,当obj严格等于L时,就返回true,如果不等于就不执行任何操作,并且进行第三步操作
			L = L.__proto__; 			//第三步操作,这条语句的意思是如果第一步判断和第二步判断都未执行,就进行这一步操作:将此时的L.__proto__再做取.__proto__处理,然后返回给L
										//所以其实每次执行完while循环后只要没返回true或false,L都会发生改变(沿原型链向上找一级隐式原型),下次再进入while时L都不是上一次进入while时的L了
	    } 
	}
</script>
所以会有如下情况发生:
<script type="text/javascript">
	function Foo(){};
	//任何函数instanceof Object都返回true
	console.log(Object instanceof Object);		//打印 	true
	console.log(Function instanceof Object);	//打印 	true
	console.log(Number instanceof Object);		//打印 	true  
	console.log(Foo instanceof Object);			//打印 	true 
	//任何函数instanceof Function都返回true
	console.log(Object instanceof Function);	//打印 	true
	console.log(Function instanceof Function);	//打印 	true 
	console.log(String instanceof Function);	//打印 	true
	console.log(Foo instanceof Function);		//打印 	true 

	console.log(Number instanceof Number);		//打印 	false 
	console.log(String instanceof String);		//打印 	false 
	console.log(Foo instanceof Foo);			//打印 	false
</script>
【4】在执行new 构造函数操作后,this就是构造函数的一个实例了,如果没有new关键字,直接执行函数体,则this不是函数体的实例
所以可以使用instanceof方法判断是否启用了new关键字:
例:
<script type="text/javascript">
	function Foo(){(this instanceof Foo) ? console.log('yes') : console.log('no')}

	new Foo();								//打印 	yes
	Foo();									//打印 	no
</script>
在每个函数内部，当该函数被调用时，将会创建一个名为arguments的对象，其中包含了传递给该函数的所用参数，同时，arguments对象中有一个名为calllee的属性，该属性指向被调用的函数
<script type="text/javascript">
	function Test(){(this instanceof arguments.callee) ? console.log('是') : console.log('不是')}

	new Test();								//打印 	是
	Test();									//打印 	不是
</script>
*************************************************************
*                                                           *
*   为什么用Object.prototype.toString.call(obj)检测对象类型  	*
*                                                           *
*************************************************************
<script type="text/javascript">
	console.log(Object.prototype.toString.call("jerry"));			//[object String]
	console.log(Object.prototype.toString.call(12));				//[object Number]
	console.log(Object.prototype.toString.call(true));				//[object Boolean]
	console.log(Object.prototype.toString.call(undefined));			//[object Undefined]
	console.log(Object.prototype.toString.call(null));				//[object Null]
	console.log(Object.prototype.toString.call({name: "jerry"}));	//[object Object]
	console.log(Object.prototype.toString.call(function(){}));		//[object Function]
	console.log(Object.prototype.toString.call([]));				//[object Array]
	console.log(Object.prototype.toString.call(new Date));			//[object Date]
	console.log(Object.prototype.toString.call(/\d/));				//[object RegExp]
	function Person(){};
	console.log(Object.prototype.toString.call(new Person));		//[object Object]
</script>
为什么这样就能区分呢？先了解一下toString方法的用法：toString()方法返回反映这个对象的字符串。
那为什么不直接用obj.toString()呢？
<script type="text/javascript">
	var obj = {};
	console.log(obj.toString());			//[object Object]
	console.log({}.toString());				//[object Object]
	console.log("jerry".toString());		//jerry
	console.log((1).toString());			//1
	console.log([1,2].toString());			//1,2
	console.log(new Date().toString());		//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
	console.log(function(){}.toString());	//function (){}
	console.log(null.toString());			//error
	console.log(undefined.toString());		//error
</script>
同样是检测对象调用toString方法,为什么obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样：
这是因为toString为Object的原型方法，而Array,function等类型作为Object的实例,都重写了toString方法。
不同的对象类型调用toString方法时，根据原型链的知识，调用的是对应的重写之后的toString方法(比如function类型返回内容为函数体的字符串，Array类型返回元素组成的字符串...)
而不会去调用Object上原型toString方法（返回对象的具体类型）
所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型,因此，在想要得到对象的具体类型时，应该借用Object上原型toString方法。
我们可以验证一下，将数组的toString方法删除，看看会是什么结果:
<script type="text/javascript">
	var arr =[1,2,3];
	console.log(Array.prototype.hasOwnProperty("toString"));//true 	,说明toString()方法是arr原型(arr.__proto__或Array.prototype)上的自有属性
	console.log(arr.toString());							//1,2,3
	delete Array.prototype.toString;						//delete操作符可以删除实例属性
	console.log(Array.prototype.hasOwnProperty("toString"));//false ,说明此时toString()方法已经从arr原型(arr.__proto__或Array.prototype)上的自有属性上删除了
	console.log(arr.toString());							//"[object Array]"
															//删除了Array的toString方法后,同样再采用arr.toString()方法调用时就不再有屏蔽Object原型方法的实例方法了
															//因此沿着原型链,arr最后调用了Object的toString方法,返回了和Object.prototype.toString.call(arr)相同的结果。
</script>
由于toString()是对象的方法，所以内部构造应该是这样的: (调用对象).toString = function (){...}
又由于调用对象在调用toString方法之前，系统内部是不知道调用对象的类型的，而不同的Object实例(比如Array ，function，Number等)又重新改写了toString方法，
所以为了能让调用实例能正确调用toString方法，在toString方法的函数体内部一定会有this指针，并且this指针指向的数据才是实际需要被传入的参数，
默认情况下this指针指向自己(谁调用的toString方法,this就指向谁),obj(调用对象)只是确定了应该使用哪种Object实例化后的toString方法。
例：
<script type="text/javascript">
	console.log('abc'.toString.call('def'));	//打印def，"abc"只是告诉系统要调用String里的toSrtring方法
												//'def'才是实际调用这个方法的参数(因为此时this指向了'def')
	console.log(true.toString.call(false));		//打印false，true只是告诉系统要调用Boolean里的toSrtring方法
												//false才是实际调用这个方法的参数(因为此时this指向了false)
	console.log((123).toString.call(789));		//打印789，123只是告诉系统要调用Number里的toSrtring方法
												//789才是实际调用这个方法的参数(因为此时this指向了789)
</script>
**********************************************************
阿里巴巴题：
执行完后x，y，z的值分别是什么？
<script type="text/javascript">
	var x = 1,y = z = 0;	//var y = z = 0 相当于 z = 0; var y = z; z没有用 var 声明，所以是全局的。
	function add(n){ return n = n + 1;}
	y = add(x);
	function add(n){		//此处的function add(n){ return n = n + 3; }由于语句靠后,所以会覆盖前面写的function add(n){ return n = n + 1; }
		return n = n + 3;
	}
	z = add(x);
	console.log(x);			//打印 	1
	console.log(y);			//打印 	4
	console.log(z);			//打印 	4
</script>
***********************************************************
逗号操作符，返回逗号最后面的数据：
<script type="text/javascript">
	var num = (1,2);
	console.log(num);		//打印2
	var k = (
		function f(){
			return '1';
		},
		function g(){
			return 2;
		}
	)();
	console.log(k);			//打印2 意思是把函数g赋给变量f后立即执行		
</script>
************************************************************
写出下列程序的执行结果：
<script type="text/javascript">
	var x = 1;
	if (function f(){}){	//由于这里function f(){}被括号括起来了，所以被转化成了函数表达式，所以系统也不认为(function f(){})语句是一条函数声明语句
							//函数表达式会自动忽略函数名，所以f就找不到了
		x += typeof f;
	}
	console.log(x);			//打印1undefined
</script>
**************************************************************
以下哪些表达式返回的结果为true：
<script type="text/javascript">
	//A
	undefined == null       //true; undefined == null,且不等于不大于不小于0
	//B
	undefined === null 		//false
	//C
	isNaN('100')			//false
</script>
**************************************************************
isNaN()函数的内部逻辑：
<script type="text/javascript">
	function isNaN(num){
		ret = Number(num);	//使用Number函数判断num是不是NaN，结果返回给ret
		ret += "";			//把ret转换为字符串形式然后再做判断
							//因为NaN连自己都不等于，所以要转成字符串形式再做判断
		if (ret == "NaN"){
			return true;
		}else{
			return false;
		}
	}
	//ES6
	var isNaN = (num) => (Number(num) + '' == 'NaN')? true : false
</script>
**********************************************************
引用值比较的是地址：
<script type="text/javascript">
	var obj = {};
	var obj = obj1;
	//控制台输入 obj1 == obj;	返回true
	//控制台输入 obj1 === obj;	返回true
	//控制台输入 {} == {};    	返回false，这两个对象的地址不一样所以返回false
</script>
**********************
*					 *
*        this        *
*					 *
**********************
https://www.jb51.net/article/77519.htm

this 不是变量,不是属性,不能为其赋值,它始终指向调用它的对象。
只要记住最重要的一条即可 '它始终指向调用它的对象'，所以找到调用this的对象，就知道this到底指向谁了
例1:
<script type="text/javascript">
	console.log(this === window);			//打印 	true,	现在调用它的对象是window了
</script>
例2:
<script type="text/javascript">
	var test = function(){
  		console.log(this === window);
 	}
	test();									//打印 	true,	现在调用它的对象还是window
</script>
例3:
<script type="text/javascript">
	var test = function(){
  		console.log(this === window);		//打印 	false,	记住"this 始终指向调用它的对象"，
		console.log(this);					//打印 	test{}
 	}
	new test();
	//例1.处调用该段代码的直接对象是全局对象，即"window" 
	//例2.处虽然是函数，但是调用其的仍然是'window'(不要弄混了，函数虽然是对象，但是调用它的是另一个对象)
	//例3.处使用了'new' 这时其实已经发生变化了，这是一个构造函数
	//构造函数在创建时,会创建一个新的空对象，即'new test()'创建了一个新的对象
	//然后再由这个对象指向函数"test"中的代码，因此此时this不在是window对象，而是该构造函数创建的新对象test{}
</script>
例4：
<script type="text/javascript">
	Person.prototype = {
		name : "a",
		sayName : function(){
			console.log(this.name);
		}
	}
	function person(){
		this.name = "b";
	}
	var person = new Person();
	//控制台输入person.sayName(),输出b，
	//控制台输入person.prototype.sayName(),输出a，
</script>

<script type="text/javascript">
	var test = {
  		a : 1,
  		b : function(){
   			console.log(this === test)
  		}
	}										//打印 true
	test.b();
</script>

<script type="text/javascript">
	var test ={
  		a : 1,
  		b : function(){
  			console.log(this);				//打印  	{a: 1, b: ƒ}
   			console.log(this === test);		//打印 	true,	引用值比较的是地址
   			console.log(this === test1);	//打印 	true,	引用值比较的是地址
  		}
 	}
	var test1 = test;						//此时'test1'变量被赋值为'test',但是'test'是一个引用值(test是一个对象),所以test1并没有产生新的对象，而只是引用了test对像,两个都指向同一个对象
	test1.b();								//打印 	true 	//true
	test.b();								//执行test1.b()与执行test.b(),两次执行的结果是一样的
</script>
引用值举例：
<script type="text/javascript">
	var test ={
  		a : 1,
  		b : function(){}
 	}
	var test1 = test;
	test.a = 2;
	console.log(test1.a);					//打印 	2
</script>
例5:
<script type="text/javascript">
	var test = {
  		a : 1,
  		b : {
   			b1 : function(){
    				console.log(this === test.b);	//打印 	true
    				console.log(this === b);		//报错 	Uncaught ReferenceError: b is not defined, ES5规范里没有块级作用域
   				}
  			}
 		}
	test.b.b1();
</script>
例6:
<script type="text/javascript">
	var test = function(){
  		var innerTest = function(){
   			console.log(this === window);		//打印 	true
   			var innerTest1 = function(){
    			console.log(this === window);	//打印 	true
   			}
   			innerTest1();						//这里面所有的innerTest函数都是'window'对象调用的,即使你嵌套一百层,调用各个函数的都是'window'对象
  		}
  		innerTest();
 	}
	test();
</script>
call/apply方法可以改变this指向:
例7:
<script type="text/javascript">
	var test = function(){
  		console.log(this === test1);			//打印 	true
 	};											//call/apply函数的作用就是:'调用一个对象的一个方法,然后以另一个对象替换当前对象'
 	var test1 = {};								//所以'window' 对象已经被替代为'test1'对象
	test.apply(test1);
</script>
例8:
<script type="text/javascript">
	var test = function(){}			//声明test是一个函数
 	var my = function(){			//声明my是一个函数
  		this.a = function(){
   			console.log(this === mytest2);		
  		}
	}
 	var mytest = new my();			//声明mytest是一个由my函数实例化后的对象
 	test.prototype = mytest;		//把mytest对象值赋值给test.prototype(此时test.prototype与mytest指向共同的地址)
 	var mytest2 = new test();		//声明mytest2是一个由test函数实例化后的对象

 	mytest2.a();					//执行mytest2.a(),打印 	true
</script>
解释原因:
<script type="text/javascript">
	var test = function(){}
 	var my = function(){
  		this.a = function(){
   			console.log(this === mytest2);
   			console.log(this);		//写这条语句是为了方便追踪每一步执行后的this指向
  		}
	}
 	var mytest = new my();			//这一步其实执行了my函数,但是执行my函数也并没有打印任何东西,因为my函数内的this.a方法才有打印命令
 									//执行完这一步之后mytest.__proto__ == my.prototype
 	console.log(mytest);			//打印 	my {a: ƒ}
 	mytest.a();						
 									//打印 	false
 									//打印  	my {a: ƒ}
 									//这一步执行my函数内的this.a方法,此时this指向mytest

 	test.prototype = mytest;		//把mytest对象值赋值给test.prototype(test.prototype与mytest指向共同的地址)
 	console.log(test); 				//打印 	ƒ (){}
 	test.a();						//报错 	Uncaught TypeError: test.a is not a function , 因为test现在是一个函数而不是对象,只有对象才可以调用自己的方法,而函数不可以

 	var mytest2 = new test();		//这一步其实执行了test函数,但是执行test函数也并没有打印任何东西,因为test函数现在是空函数ƒ (){ }

	console.log(mytest2);			//打印 	test {}
 	mytest2.a();					//打印 	true
 									//打印  	test {}
 									//这一步执行mytest2函数内的this.a方法,此时this指向mytest2,打印 	true
</script>

※※综上可以看出,在执行带有this的方法时,谁出现在'.a()'语句的前面,this就指向谁,比如执行'mytest.a();'语句时,是'mytest'出现在'.a()'语句的前面,所以this就指向了mytest;
而在执行'mytest2.a();'语句时,是'mytest2'出现在'.a()'语句的前面,所以this就指向了mytest2。其实总而言之就是谁(专指对象)调用的'this'所在的方法(function(){...}),this就指向谁。
【1】 函数预编译过程中(AO里)this指向window
但在有new出现的函数里，会在函数体内隐式声明this改变this指向,以test函数为例：
	var this = Object.create(test.prototype);
其实也就相当于被构造的new test()对象里
	{	
		...
		__proto__ : test.prototype
	}
例：
【1.1】没有new的函数:
<script type="text/javascript">
	function test(c, d){
		var a = 123;
		function b(){}
	}
	test(1,2);
	//AO{
	//		a : undefined,
	//		b : function (){},
	//		c : 1,
	//		d : 2,
	//		arguments : [1,2],
	//		this : window
	//	}
</script>
【1.2】有new的函数:
<script type="text/javascript">
	function test(c,d){
		var a = 123;
		function b(){}
	}
	//AO{
	//		a : undefined,
	//		b : function (){},
	//		this : Object.create(test.prototype);
	//	}
	new test();
	//var this = Object.create(test.prototype);
	//或是可以看成
	//	{
	//		...
	//		__proto__ : test.prototype
	//	}
</script>
【2】 全局作用域(GO)里this指向window
【3】 call/apply可以改变函数运行时this指向
【3.1】预编译时this指向window
【3.2】谁调用的this,this就指向谁
【3.3】call改变this指向
【3.4】全局this指向window
<script type="text/javascript">
	function test(){
		var num = 123;
		function a(){}
	}
	test()		//此时执行test(),相当于执行test.call()
				//AO{
				//		num : undefined,
				//		a   : function(){},
				//		arguments : {} 	,系统内部隐式生成
				//		this : window 	,系统内部隐式生成
				//	}	
</script>
使用this实现在Student构造函数内调用Person方法:
<script type="text/javascript">
    function Person(name, age){
        this.name = name;
        this.age = age;
    }
    Person.prototype.say = 'Hi';
    function Student(name, age, sex){
        //此时相当于 var this = Object.create(Student.prototype);
        Person.call(this, name, age);
        this.sex = sex;
    }
    var student = new Student('cheng',18, 'male');
    //控制台输入studnet.say会报错,因为Person.call(this, name, age)语句只会让this调用Person函数本体,而无法继承Person函数原型链上的属性和方法
</script>
【4】 obj.func(); func()里面的this指向obj
<script type="text/javascript">
	var obj = {
		a : function(){
			console.log(this.name);
		},
		name : 'abc'
	}
	obj.a();						//打印 	'abc'
</script>
*******************************************************************
this考题：
	a.say()是a调用say方法
	a.say是引用一个函数体
<script type="text/javascript">
	var name = '222';
	var a = {
		name : '111',
		say :　function(){
			console.log(this.name);
		}
	}
	var fun = a.say;				//a.say只是一个函数体，a.say代表对a对象里say函数的引用，代表了一个函数体
									//让fun引用a.say里的函数
	fun()							//打印 	222，让fun在全局作用域里执行，此语句相当于window.fun()
	a.say()							//打印 	111，在a里调用say方法执行
	var b = {
		name : '333',
		say : function(fun){
			fun();					//函数体内部是fun()，而不是this.fun()或var fun()
		}
	}
	b.say(a.say);					//打印 	222，b调用say方法，此时如果say方法里有this,则this会指向b(但这里的say方法内没有this),然后向b.say方法里传入参数，参数是a.say引用的函数体
									//然后执行这个函数体，b.say的函数体里并没有说是this.fun()或var fun()
									//而是直接执行参数函数体,也就是说并没有人调用这个函数，所以由window调用
									//函数体内的函数未声明直接调用直接走预编译，所以this指向window
									//切记:谁.fun(),this就指向谁
	b.say = a.say;
	b.say();						//打印 	333
</script>
**********************************************************************
课堂练习题：
【1】
<script type="text/javascript">
	var foo = '123';
	function print(){
		var foo = '456';
		this.foo = '789';
		console.log(foo);			//第二步打印,打印 456,在这里print只是一个普通函数,console.log(foo)会打印print()函数内部的局部变量var foo = '456'
									//此代码片段中因为没有new出现,所以this指向window,所以this.foo所指向的是全局变量下的foo,执行this.foo = '789';语句时,会把全局变量下的foo赋值为789
	}
	console.log(foo);				//第一步打印,打印 123(因为print还没执行)
	print();						//执行print()								
	console.log(foo)				//第三步打印,打印 789
</script>
【2】
<script type="text/javascript">
	var foo = 123;
	function print(){
		this.foo = 234;
		console.log(foo);			//打印 	234,先找自己的AO里有没有foo，发现没有，然后上GO里找
	}
	console.log(foo);				//打印 	123,print()函数执行前
	print();
	console.log(foo);				//打印 	234,print()函数执行后
</script>
【3】
<script type="text/javascript">
	var foo = 123;
	function print(){
		//var this = Object.creat(print.prototype)
		this.foo = 234;
		console.log(foo);			//打印 	123,注意:这里打印的是foo而不是this.foo。其实从本质上说,在出现new关键字后,this.foo属性就和print函数没什么关系了,他就被放入到了另一个作用空间中(函数的构造空间中)
	}								//如果在代码片段里没有new关键字的情况下,this.foo属性就变成全局下的foo属性
	new print();
	console.log(foo);				//打印 	123
</script>
执行test()和new test()的结果分别是什么？
【4】
<script type="text/javascript">
	var a = 5;
	function test(){
		//var this = {
		// 	...
		//	__proto__ : test.prototype
		//}
		a = 0;
		console.log(a);
		console.log(this.a);
		console.log(this);
		var a;			//a声明
		console.log(a);
	}

	//执行 	test();		//打印 	0 
						//打印 	5， this指向window
						//打印  window
						//打印 	0
						//打印  undefined  	因为如果没写返回值默认会返回undefined

	//执行 	new test();	//打印 	0 
						//打印 	undefined  	new之后的AO{a : 0,this : test{}}
						//打印  test{}
						//打印 	0
						//打印  test{}		new关键字之后调用test()会默认返回新的实例化后的对象
</script>
【5】
<script type="text/javascript">
	var foo = 123;
	function print(){	//在构造函数print()里,只有带this的属性才会附给被构造出的对象(比如this.a)
		this.a = 234;
		b = 0;	
	}
	var c = new print();
	console.log(c);		//打印 	print {a: 234}
	console.log(c.a);	//打印 	234
	console.log(c.b);	//打印 	undefined
</script>
【6】
<script type="text/javascript">
	function print(){
		var marty = {
			name : "marty",
			printName : function(){console.log(this.name);}		
		}
		var test1 = {name : "test1"};
		var test2 = {name : "test2"};
		var test3 = {name : "test3"};
		test3.printName = marty.printName;	//执行这一步,只是让test3.printName方法引用marty.printName所指向的函数体
		var printName2 = marty.printName.bind({name : '123'});

		marty.printName.call(test1);		//打印 	test1
		marty.printName.apply(test2);		//打印 	test2
		marty.printName();					//打印 	marty
		printName2();						//打印 	123
		test3.printName();					//打印 	test3
	}
	print();
</script>
【7】
<script type="text/javascript">
	var bar = {a : '002'};
	function print(){
		bar.a = 'a';
		Object.prototype.b = 'b';
		return function inner(){
			console.log(bar.a);				//打印 	a
			console.log(bar.b);				//打印 	b
		}
	}
	print()();
	console.log(bar.a);						//打印 	a
	console.log(bar.b);						//打印 	b
</script>
******************************
*                            *
*         三目运算符? :       *
*                            *
******************************
条件判断? 是:否 并且会返回值
例1:
<script type="text/javascript">
	var num = 1 > 0 ? ("10" > "9" ? 1 : 0) : 2;
	console.log(num);						//打印0，先判断括号里的("10" > "9" ? 1 : 0)，字符串逐位比较ASCII码"1"<"9"所以结果是false,返回0
											//然后判断(1 > 0 ? 0 : 2)结果是true,返回0
</script>
例2:
<script type="text/javascript">
	var num = 1 > 0 ? ("10" > 9 ? 1 : 0) : 2;
	console.log(num);						//打印1，先判断最里面括号的("10" > 9 ? 1 : 0),">"两边有数字先隐式性转换为数字，10 > 9,结果是true,返回1
											//然后判断(1 > 0 ? 1 : 2)结果是true,返回0
</script>
*************************
*                       *
*          克隆        	*
*                       *
*************************
浅层克隆(不拷贝引用值到新地址):
<script type="text/javascript">
	var obj = {
		name : 'abc',
		age :　123,
		sex : 'male'
	}
	var obj1 = {};
	function clone(origin, target){
		var targrt = target || {}; 			//容错,如果用户给了target就用用户的，如果没给就创建一个空对象,防止用户忘传target
		for(var prop in origin){
			target[prop] = origin[prop];
		}
		return target;
	}
	clone(obj, obj1);
</script>
深层克隆(拷贝引用值到新地址):
<script type="text/javascript">
	// 深克隆结构
	// 遍历对象 for(var prop in obj),而且数组和对象都可以用
	// 1.判断是不是原始值，是原始值直接拷贝 (使用typeof()方法与'object'作对比)
	// 2.判断是数组还是对象 instanceof、constructor(这两种方法不能跨域判断),但toString()方法可以
	// 3.建立相应的数组或对象
	function deepClone(origin, target){
		var target = target || {},								//容错,如果用户给了原料就用用户的，如果没给就创建一个空对象,防止用户忘传target
			toStr = Object.prototype.toString,
			arrStr = "[boject Array]";
		for (var prop in origin){
			if(origin.hasOwnProperty(prop)){					//判断是不是自有属性,如果是的话返回true(避免拷贝原型链上的属性)

				if(origin[prop] !== null && typeof(origin[prop]) == 'object') {	//如果是引用值且不是null执行下面判断并进入递归

					target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {}		//这条语句等价于:	
																					//		if (toStr.call(origin[prop]) == arrStr) {
																					//			target[prop] = [];
																					//		}
																					//		else{
																					//			target[prop] = {};
																					//		}
					deepClone(origin[prop], target[prop]);
				}else{
					target[prop] = origin[prop];				//如果是原始值直接拷贝
				}
			}
		}
		return target;											//返回target
	}
</script>
两种深克隆的区别:
<script type="text/javascript">
	var obj = {
		"name" :　"abc",
		"age" : "123",
		"card" : ["visa","master",["icbc", "cbc"]],
		"wife" : {
			"name" : "bcd",
			"son" : {
				"name" : "aaa",
				"feature" : {
					"wight" : "75kg",
					"height" : "188cm"
				}
			}
		}
	},
	obj1 = {},
	obj2 = {};
	//深克隆(1):
	function deepClone1(origin, target){
		var target = target || {},
			toStr = Object.prototype.toString;
		if(origin != null){
			for(var prop in origin){
				if(origin.hasOwnProperty(prop)){
					var src, copy = origin[prop];
					if(copy !== 'null' && typeof copy == 'object'){
						src = toStr.call(copy) == "[object Array]" ? [] : {};
						target[prop] = deepClone1(copy, src);
					}else{
						target[prop] = copy;
					}
				}
			}
		}
		console.log(target);
		return target; 
	}
	//深克隆(2):
	function deepClone2(origin, target){
		var target = target || {},
			toStr = Object.prototype.toString;
		if(origin != null){
			for (var prop in origin){
				if(origin.hasOwnProperty(prop)){					
					if(origin[prop] !== 'null' && typeof(origin[prop]) == 'object') {	
						target[prop] = toStr.call(origin[prop]) == "[object Array]" ? [] : {};	
						deepClone2(origin[prop], target[prop]);
					}else{
						target[prop] = origin[prop];				
					}
				}
			}
		}
		console.log(target);
		return target;							
	}
	deepClone1(obj, obj1)
	deepClone2(obj, obj2)
	//打印 	["icbc", "cbc"] 												-->打印的是最先出现的里面全为原始值的引用值(card数组里的["icbc", "cbc"]数组)
	//打印	["visa", "master", Array[2]] 									-->打印的是card所对应的数组(从上次打印完的结果往外层找里面全为原始值的引用值)
	//打印	Object {wight: "75kg", height: "188cm"}							-->打印的是feature所对应的对象
	//打印 	Object {name: "aaa", feature: Object}							-->打印的是son所对应的对象
	//打印 	Object {name: "bcd", son: Object}								-->打印的是wife所对应的对象
	//打印	Object {name: "abc", age: 123, card: Array[3], wife: Object}	-->打印的是是最终obj所对应的对象

	//分析：
	//两种克隆的区别在于深克隆(1)需要写target[prop] = deepClone(src, copy);
	//而深克隆(2)在3743行直接写deepClone(origin[prop],target[prop]);就行,而不需要写"target[prop] = "
	//原因就在于深克隆(1)在3716行写了var src = target[prop];此时src会变为undefined(因为target[prop]此时确实是undefined),
	//而之后的语句里src就被重新赋值成了 src = [];或src = {};此时的src就已不再是 target[prop]了
	//所以在3724行递归执行deepClone(src,copy)时并不是把target[prop]作为目标地址传入的,
	//所以就无法在不接收deepClone(src,copy)返回值的情况下通过递归传入target[prop]的方式更改target[prop]的值,
	//而是要通过使用target[prop]接收的方式来更改target[prop]的值。
	//而深克隆(2)在每次递归调用deepClone函数时,都是把target[prop]作为接收克隆的地址传入到了deepClone中,
	//所以不需要再用target[prop]去给递归函数做接收了,而是在执行deepClone函数时,target[prop]就直接被更改了
	//两种深克隆的思想在本质上是完全不同的,方法1是先使用src来替代目标体(新对象)中相对应于被克隆体(老对象)中的[prop]属性的类型值,
	//如果老对象中是{},就把src设为空对象,如果是[],就把src设为空数组,如果是原始值则把值返回为target[prop]
	//然后再递归调用deepClone(src,copy),也就是把src作为类型相同的新空对象([]/{})来接收copy(老对象)里的值,
	//接下来还是:如果老对象中是{},就把src设为空对象,如果是[],就把src设为空数组,如果是原始值则返回为target,如此递归
	//然后不断把target返回出来,使用target[prop]来接收上一次递归的返回值target,等到最后返回出来的target就是需要的新对象
</script>
*************************
*                       *
*         数组          	*
*                       *
*************************
数组的定义(生成数组的方法):
【1】new Array(length/content);		//var arr = new Array();
【2】字面量							//var arr = [];

这两种定义的区别(就一个)：
如果定义一个由两个元素或两个元素以上组成的数组则没有区别：
<script type="text/javascript">
	var arr = [1,2,3]
	var arr1 = new Array(1,2,3)
	//arr与arr1完全等价
</script>
但如果给new Array里只传一个参数,则这个参数反映的是数组长度,而不是数组的第一个元素的值：
<script type="text/javascript">
	var arr = new Array(10);	//控制台输入arr会输出[undefined × 10]，而不是[10]
</script>
******************************************************
数组能使用的一切方法都来源于Array.prototype
数组的读和写:
【1】不可以溢出读,arr[num]:
<script type="text/javascript">
	var arr = [1,2];
	console.log(arr[3]);		//打印 	undefined
</script>
【2】可以溢出写,arr[num] = xxx:
<script type="text/javascript">
	var arr = [1,2];
	arr[4] = 1;
	console.log(arr[4]);			//打印 	1
	console.log(arr[3]);			//打印 	undefined
    console.log(arr);               //打印    (5) [1, 2, empty × 2, 1], 0 : 1, 1 : 2, 4: 1, length: 5, __proto__: Array(0)
</script>
********************************************************
数组的常用方法(分为改变原数组的方法和不改变原数组的方法):

一、改变原数组的方法:
【添加】push(从数组末尾添加元素，可一次性添加多个)
【删除】pop(删除数组最后一位元素，不可以传参数，一次只删一个)
【添加】unshift(从数组前面添加元素，可一次添加多个)
【删除】shift(从数组前面删除元素，一次只能删一个)
【逆转】reverse(把原数组逆转顺序)
【切片】splice(从第几位开始(可以写负数，代表倒数第几位),截取多少的长度,在切口处添加新的数据)。注意,splice方法的返回值是被截取的切片的值
在数组中splice方法有增、删、改的多种用处:
<script type="text/javascript">
		var list = [];
        list.push(1);
        list.push(2);
        list.push(3);
        console.log(list); 					//打印 	(3) [1, 2, 3]    
        //删除
        console.log(list.splice(0,1));  	//打印  [1] 	,返回一个从下标为0开始,长度为1的数组切片,并将这个切片从数组中删除
        console.log(list); 					//打印  	(2) [2, 3]
        console.log(list.splice(0,2));  	//打印  (2) [2, 3] 	,返回一个从下标为0开始,长度为2的数组切片,并将这个切片从数组中删除
        console.log(list); 					//打印  	[]  
        //替换
        list = [1,2,3];
        console.log(list.splice(0,1,4)); 	//打印  	[1]	,返回一个从下标为0开始,长度为1的数组切片,并将这个元素在原数组的位置中替换成4
        console.log(list);  				//打印  	(3) [4, 2, 3]
        console.log(list.splice(0,2,4)); 	//打印  	(2) [4, 2] 	,返回一个从下标为0开始,长度为2的数组切片,并将这个元素在原数组的位置中替换成4
        console.log(list);  				//打印  	(2) [4,3]      
        //添加
        list = [1,2,3];  					
        console.log(list.splice(1,0,5));  	//打印  	[] 	,返回一个从下标为1开始,长度为0的数组切片,并将这个元素在原数组的位置中替换成5,
        									//其实就是在原数组下标为1处添加一项5
        console.log(list);        			//打印  	(4) [1, 5, 2, 3]
        //想把数组arr = [1,2,3,5]改成arr = [1,2,3,4,5]
        arr = [1,2,3,5];
		arr.splice(3,0,4);
		console.log(arr); 					//打印 	(5) [1, 2, 3, 4, 5]
</script>
在使用splice方法时,支持输入负数,如果输入正数,就正常查找,如果输入负数就意味着是倒数第几个数,比如:
<script type="text/javascript">
	var arr = [1,2,3,4];
	console.log(arr.splice(1,2)); 	//打印 	(2) [2, 3] 	从索引位第1位开始截取,向后截取2个长度的数据
	console.log(arr)				//打印  	(2) [1, 4]
</script>

<script type="text/javascript">
	var arr = [1,2,3,4];
	console.log(arr.splice(-2,2));	//打印 	(2) [3, 4] 	从索引位倒数第2位开始截取,向后截取2个长度的数据
	console.log(arr)				//打印  	(2) [1, 2]
</script>
那么JavaScript是如何实现负数修正的?
如果输入的数值大于0,就返回这个数+ 0;如果输入的数小于0,就返回这个数+ this.length:
<script type="text/javascript">
	function(position){
		return position += (position > 0 ? 0 : this.length);
	}
</script>
【排序】sort()支持两种方法排序:
【1】直接使用sort()方法是按逐位ASCII码从低到高排：
<script type="text/javascript">
	arr = [1,3,5,7,4,10]
	arr.sort()	
	console.log(arr)				//打印 	(6) [1, 10, 3, 4, 5, 7]
</script>
【2】sort()还支持自定义接口:
【2.1】必须写俩形参
【2.2】看返回值:
	1）当返回值为负数时，那么前面的数放在前面
	2）为正数，那么后面的数在前
	3）为0，不动
例:
<script type="text/javascript">
	var arr = [2,10,20,13,4,8,9];
	arr.sort(function(a,b){
		if(a > b){
			return 1;
		}else{
			return -1;
		}
	});
</script>
简化:
<script type="text/javascript">
	var arr = [20,2,10,13,4,8,9];
	arr.sort(function(a, b){
		return a - b;		//升序;
		return b - a;		//降序;
	})
</script>
给有序数组乱序：
<script type="text/javascript">
	var arr = [0,1,2,3,4,5,6,7,8,9,10]
	arr.sort(function(a, b){
		return Math.random - 0.5;
	});
</script>
给数组按字符串长度排序：
<script type="text/javascript">
	var arr = ['f','sd','sdgs'.'afarwsefgsd','adaafff','asafa','asdssds']
	arr.sort(function(a,b){
		return a.length - b.length;
		//在sort()里还可以使用自定义的方法作为自定义接口,比如function(a) - function(b);
	})
</script>

二、不改变原数组：
【连接】concat
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	console.log(arr.concat(0,0,0)); 	//打印 	[1,2,3,4,5];
	console.log(arr); 					//打印 	[1,2,3,4,5,0,0,0];
</script>
【变字符串】toString
【截取】slice(从该位开始截取(可以填负数),截取到该位)
将arguments转换成数组可以用slice方法:
<script type="text/javascript">
	function a(){
		target = Array.prototype.slice.apply(arguments);  	//数组能使用的一切方法都来源于Array.prototype
		console.log(target);								//控制台输入a(1,4,7)会返回[1,4,7]
	}
</script>
【链接】join(链接符(字符串形式,不传参数默认是按逗号连)),返回值是字符串形式：
<script type="text/javascript">
	var arr = [1,2,3,4];
	console.log(arr.join('-'));		//打印 	"1-2-3-4"
</script>
将数组转化成字符串可以用arr.join('')方法:
<script type="text/javascript">
	var arr = [1,2,3,4];					
	console.log(arr.join(''));									//打印 	"1234"
	console.log(typeof(arr.join('')));							//打印 string
	console.log(arr);											//打印 	(4) [1, 2, 3, 4]	,使用join方法不改变原数组
	console.log(Object.prototype.toString.call(arr)); 			//打印 	[object Array]	,使用join方法不改变原数组
</script>
该方法与字符串的split方法互逆,使用split方法会返回数组：
<script type="text/javascript">
	var str = "1-5-9-7-5-3";
	str.split('-');												//返回["1", "5", "9", "7", "5", "3"]
</script>
将字符串转化成数组可以用str.split('')方法,使用split方法不改变原字符串:
<script type="text/javascript">
	var str = '123456789';					
	console.log(str.split(''));									//打印 	(9) ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
	console.log(Object.prototype.toString.call(str.split('')));	//打印 	[object Array]
	console.log(str);											//打印 	123456789 	,使用split方法不改变原字符串
	console.log(typeof(str)); 									//打印 	string 	,使用split方法不改变原字符串
</script>
【查找元素出现的位置】indexOf
<script type="text/javascript">
	var arr = [1,8,4,9,3,5,1,9,7,6];
	console.log(arr.indexOf(9)); 	//打印3，表示在第3号索引位找到9
	console.log(arr.indexOf(19));	//打印-1，表示没找到19
</script>
【查找元素最后出现的位置】lastIndexOf
<script type="text/javascript">
	var arr = [1,8,4,9,3,5,1,9,7,6];
	console.log(arr.lastIndexOf(9)); 	//打印7，表示在第7号索引位最后找到9
</script>
【数组元素的'与'和'或'】every/some
<script type="text/javascript">
	var arr = [1,5,9,3,4,8,6,2,7];
	console.log(arr.every(function(ele){return ele > 3})); 	//打印 	false,	表示并不是所有的ele都大于3
	console.log(arr.every(function(ele){return ele > 0}));	//打印  	true,	表示所有的ele都大于0
	console.log(arr.some(function(ele){return ele > 9}));	//打印 	false,	表示并没有ele大于9
	console.log(arr.some(function(ele){return ele > 0})); 	//打印 	true,	表示有ele大于0
</script>
********************************************************************
自己封装Array.prototype.push方法:
以下为伪代码：
<script type="text/javascript">
	Array.prototype.push = function(target){
		obj[obj.length] = target; 				//每次obj里下标和数组长度值相等的元素(也就是数组的末尾元素)被赋值成target
		obj.length ++;							//每次执行完之后obj.length +1,因为每次执行之后数组的末尾长度+1,所以每次执行完之后操作的元素都往后+1位
	}
</script>
综上,自己写push方法:
<script type="text/javascript">
	var arr = [1,2,3];
	Array.prototype.push = function(){			//因为参数个数不固定所以不写形参
		for(i = 0; i < arguments.length; i++){	//arguments.length是传入实参的长度,形参的长度决定了循环次数,比如要push[a,b,c,d,e]数组就要循环5次,所以arguments.length此时也就是5
			this[this.length] = argumnets[i];	//谁调用的this,this就指向谁,注意,这里的this指向的是调用他的数组,而不是要被push的数组
												//比如(伪代码):[1,2,3].push[a,b,c,d,e]
												//此时调用this的是[1,2,3],this指向[1,2,3],this.length就是3(length不是索引位);arguments指向的是[a,b,c,d,e],arguments.length就是5
												//this[this.length]就是数组的第3位索引(第4位)
												//每执行完一次循环后this.length都会+1(因为每次执行完循环后,数组的最后一位+1的位置都被赋了值(this[this.length] = argumnets[i]))
												//所以this.length会自动+1
		}
		return this.length 						//返回新数组的长度
	}
</script>
**********************************************************
自己封装unshift(数组首位插入元素)方法:
<script type="text/javascript">
	//类型一把传入的参数都插入
	var arr = [1,2,3,4]
	Array.prototype.unshift = function(){
		var target = Array.prototype.slice.apply(arguments);
		console.log(this);						//打印 	(4) [1, 2, 3, 4]
		console.log(target);					//打印 	(3) [1, 4, 7]
		console.log(target.concat(this));		//打印 	(7) [1, 4, 7, 1, 2, 3, 4]
	}
	arr.unshift(1,4,7);
	//类型二只插入传入参数的第一位
	var arr = [1,2,3,4];
	Array.prototype.unshift = function(){
		var target = Array.prototype.slice.apply(arguments,[0,1]);
		console.log(this);						//打印 	(4) [1, 2, 3, 4]
		console.log(target);					//打印 	[1]
		console.log(target.concat(this));		//打印 	(5) [1, 1, 2, 3, 4]
	}
	arr.unshift(1,4,7);
</script>
********************************************************
自己封装typeof()方法：
<script type="text/javascript">
	function type(target){
		var template ={
			'[object Array]' : 'array',
			'[object Object]': 'object',
			'[object Null]'  : 'null',
			'[object Number]': 'Number object',
			'[object String]': 'String object',
			'[object Boolean]':'Boolean object',
		}
		if(typeof(target) == 'object'){
			return template[Object.prototype.toString.call(target)]
		}else{
			return typeof(target);
		}
	}
	var num = new Number(123);
	var str = new String(123);
	var bol = new Boolean(123);
	console.log(num);			//打印 	Number {123}
	console.log(str);			//打印 	String {"123"}
	console.log(bol);			//打印 	Boolean {true}
	console.log(type(num));		//打印 	Number object
	console.log(type(str));		//打印 	String object
	console.log(type(bol));		//打印 	Boolean object
</script>
********************************************************
自己封装数组去重方法：
<script type="text/javascript">
	Array.prototype.unique = function(){
		var temp = {},
			arr = [],
			len = this.length;
		for (var i = 0; i < len; i++){
			if(!temp[this[i]]){
				temp[this[i]] = 1;
				arr.push(this[i]);
			}
		}
		return arr;
	}
</script>
*************************************************
自己封装字符串去重方法：
<script type="text/javascript">
	var str = 'asdfdsagasdf'
	String.prototype.unique = function(){
		var	temp = {},
			arr = [],
			len = this.length,
			result;
		for(i = 0; i < this.length; i++){
			if(!temp[this[i]]){
				temp[this[i]] = 1;
				arr.push(this[i]);
			}
		}
		result = arr.join(''); 			//这一步是将去重后的数组转化为字符串
		return result;
	}
</script>
********************************************
一个字符串由[a-z]组成,请找出该字符串中第一个只出现一次的字母
<script type="text/javascript">
	String.prototype.firstUnique = function(){
		var len = this.length,
			obj = {},
			position,
			value;
		for(i = len - 1; i >= 0; i--){ 	//倒序查找
			if(!obj[this[i]]){
				obj[this[i]] = 1;
				position = i;
				value = a[i];
			}
		}
		console.log(position);			//打印 	12
		console.log(value);				//打印 	d
	}
	a = 'qweuiyteuitudweqpwoweirityqwertyuiopoiuytrewqyuiowetioytiooweporqpq';
	a.firstUnique();
</script>
******************************************
获取字符串内的元素可以采用两种方法：
【1】直接获取法：
<script type="text/javascript">
	var str = 'abcdefg';
		//控制台输入str[2]会返回'c'
</script>
【2】将str字符串使用split方法法打散后获取：
<script type="text/javascript">
	var str = 'abcdefg';	
	strArray = str.split('');	
		//控制台输入strArray[2]会返回'c'
</script>

数组是引用值,比较两个数组是否相等,比较的是地址,但如果就想比较连个数组里的元素是否相等,可以如下封装函数:
<script type="text/javascript">
	function equalArrays(a, b){
		if(a.length != b.length) return false;				//两个长度不同的数组,会返回false
		for(var i = 0, len = a.length; i < len; i++)		//遍历循环所有元素
			if(a[i] != b[i]) return false;					//如果有任意元素不等,返回false
		return true;										//否则返回true
	}
</script>
*************************
*                       *
*         类数组        	*
*                       *
*************************
类数组:
【1】本质上是对象,但可以当数组用,类数组最重要的标志就是给对象添加length属性
【2】要有索引(数字)属性,而且必须有length属性,最好加上push方法
【3】一旦给对象加上{splice : Array.prototype.splice}后对象就长成了数组的样式
例:
给数组加上{splice : Array.prototype.splice}前:
<script type="text/javascript">
	var obj = {
		'0' : 'a',
		'1' : 'b',
		'2' : 'c',
		length : 3,
		push : Array.prototype.push,
	}	//控制台输入obj会显示{0: "a", 1: "b", 2: "c", length: 3, push: ƒ}
</script>
给数组加上{splice : Array.prototype.splice}后:
<script type="text/javascript">
	var obj = {
		'0' : 'a',
		'1' : 'b',
		'2' : 'c',
		length : 3,
		push : Array.prototype.push,
		splice : Array.prototype.splice
	}	//控制台输入obj会显示Object(3) ["a", "b", "c", push: ƒ, splice: ƒ]
</script>
【4】可以利用属性名模拟数组的特性,可以动态的增长length属性
【5】如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
【6】arguments就是一个类数组
类数组举例：
<script type="text/javascript">
	var obj = {
		'0' : 'a',
		'1' : 'b',
		'2' : 'c',
		length : 3,
		push : Array.prototype.push
	}	//控制台输入obj.push('d') 会输出 	Object {0: "a", 1: "b", 2: "c", 3: "d", length: 4}
</script>
由于数组里封装的push方法是针对length进行的操作,所以类数组里push的方法也是对length进行操作
例:
<script type="text/javascript">
	var obj = {
		'2' : 'a',		//类数组的数字属性是他的索引属性,此时obj[2] = 'a'
		'3' : 'b',
		length : 2,
		push : Array.prototype.push 
	}
	obj.push('c');
	obj.push('d');
	console.log(obj)	
	//打印 	{2: "c", 3: "d", length: 4, push: ƒ}
	//在执行 obj.push('c');语句时,此时 
	//obj{ 	'2' : 'a',
	// 		'3' : 'b',
	//		length = 2,
	//		... 
	//	}
	//根据数组的push方法,此时的 obj[obj.length](也就是obj[2])会被赋值为'c',执行完这一句后自动obj.length ++,所以执行完obj.push('c');语句后会变成；
	//obj{ 	'2' : 'c',
	// 		'3' : 'b',
	//		length = 3,
	//		... 
	//	}
	//在执行 obj.push('d');语句时和上述语句类似
</script>
arguments是类数组,typeof(arguments)返回object
例：
<script type="text/javascript">
	function test(){
		console.log(typeof(arguments));		//输出object
	}
	test();
</script>
*************************
*                       *
*        with(){}		*
*                       *
*************************
https://www.cnblogs.com/lrzw32/p/5189450.html

一、with语句作用:
with语句的作用是将代码的作用域设置到一个特定的作用域中,基本语法如下：
	with (expression) statement;
使用with关键字的目的是为了简化多次编写访问同一对象的工作，比如下面的例子：
<script type="text/javascript">
	var qs = location.search.substring(1);
	var hostName = location.hostname;
	var url = location.href;
	//这几行代码都是访问location对象中的属性,如果使用with关键字的话,可以简化代码如下:
	with (location){
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}
	//在这段代码中，使用了with语句关联了location对象，这就以为着在with代码块内部，每个变量首先被认为是一个局部变量，
	//如果局部变量与location对象的某个属性同名，则这个局部变量会指向location对象属性
</script>
二、with关键字的弊端:
前面的基本说明中，我们可以看到with的作用之一是简化代码。但是为什么不推荐使用呢？下面我们来说说with的缺点：
【1】性能问题
首先说说性能问题，关于使用with关键字的性能问题，首先我们来看看两段代码：
<script type="text/javascript">
	//第一段代码不使用with关键字：
	function func() {
	    console.time("func");
	    var obj = {
	        a: [1, 2, 3]
	    };
	    for (var i = 0; i < 100000; i++) {
	       var v = obj.a[0];
	    }
	    console.timeEnd("func");		//0.847ms
	}
	func();
	//第二段代码使用with关键字：
	function funcWith() {
	    console.time("funcWith");
	    var obj = {
	        a: [1, 2, 3]
	    };
	    var obj2 = { x : 2 };
	    with (obj2) {
	        console.log(x);
	        for (var i = 0; i < 100000; i++) {
	            var v = obj.a[0];					
	            //此时v = obj.a[0] = 1,with(obj2){...}意思是说首先会在obj2上查找是否有名为obj的属性，如果没有的话进行下一步查找，
	        	//下一步查找的作用域是与with (expression) statement同级且并列的变量，在这里面就是obj和obj2
	        }
	    }
	    console.timeEnd("funcWith");	//84.808ms
	}
	funcWith();
	//在使用了with关键字后了，代码的性能大幅度降低;第二段代码的with语句作用到了obj2这个对象上，然后with块里面访问的却是obj对象
	//有一种观点是：使用了with关键字后，在with块内访问变量时，首先会在obj2上查找是否有名为obj的属性，如果没有，再进行下一步查找
	//这个过程导致了性能的降低。但是程序性能真正降低的原因真的是这样吗？
	//我们修改一下第二段代码，修改如下：
	function funcWith() {
	    console.time("funcWith");
	    var obj = {
	        a: [1, 2, 3]
	    };
	    with (obj) {
	        for (var i = 0; i < 100000; i++) {
	            var v = a[0];
	        }
	    }
	    console.timeEnd("funcWith");	//88.260ms
	}
	funcWith();
	//这段代码将with语句作用到了obj对象上,然后直接使用a访问obj的a属性,
	//按照前面说到的观点,访问a属性时,是一次性就可以在obj上找到该属性的,但是为什么代码性能依旧降低了呢?
	//真正的原因是:使用了with关键字后,JS引擎无法对这段代码进行优化
	//JS引擎在代码执行之前有一个编译阶段,在不使用with关键字的时候,js引擎知道a是obj上的一个属性,
	//它就可以静态分析代码来增强标识符的解析,从而优化了代码,因此代码执行的效率就提高了
	//但在使用了with关键字后,js引擎无法分辨出a变量是局部变量还是obj的一个属性,因此,js引擎在遇到with关键字后,它就会对这段代码放弃优化,所以执行效率就降低了
	//使用with关键字对性能的影响还有一点就是js压缩工具,它无法对这段代码进行压缩,这也是影响性能的一个因素
</script>
【2】语义不明,难以调试
前面说到除了性能的问题,with还存在的一个缺点语义不明,难以调试,就是造成代码的不易阅读,而且可能造成潜在的bug:
<script type="text/javascript">
	function foo(obj) {
	    with (obj) {
	        a = 2;
	    }
	}
	var o1 = {
	    a: 3
	};
	var o2 = {
	    b: 3
	};
	foo(o1);
	console.log(o1.a); 		//打印 	2
	foo(o2);
	console.log(o2.a); 		//打印 	undefined
	console.log(a); 		//打印 	2,	这里打印的2是由于在执行console.log(o2.a)时o2里没有a属性,所以a变成了全局变量

	//这段代码很容易理解了,在foo函数内,使用了with关键字来访问传进来的obj对象,然后修改a属性。当传入o1对象时,因为o1对象存在着a属性,所以这样没有问题。
	//传入o2对象时,在修改a属性时,由于o2对象没有a这个属性,所以被修改的a属性则变成了全局变量。这就造成了潜在的bug。
</script>
【3】延伸分析:
<script type="text/javascript">
	var obj = {
	    x: 10,
	    foo: function () {
	        with (this) {
	            var x = 20;		//因为有了with(this){...}语句,所以执行var x = 20语句时会先找this里面(也就是obj里)有没有x属性,
	            				//发现有,直接给这个变量x赋值为20
	            var y = 30;		//因为有了with(this){...}语句,所以执行var y = 30语句时会先找this里面(也就是obj里)有没有y属性,
	            				//发现没有,则在foo函数作用域内声明一个变量y,再给y赋值为30,此时y是foo里的局部变量
	            console.log(y);	//30
	        }
	    }
	};
	obj.foo();
	console.log(obj.x);		//打印 	20
	console.log(obj.y);		//打印 	undefined, 	在这里y是with环境下的局部变量,此时y的作用域是foo函数体内部,obj里无法访问y

	//在这段代码中，分别输出30，20，undefined, 涉及的知识点也比较多：with关键字，this关键字，变量提升等等，这里一一解释一下:
	//【1】this关键字
	//this关键字始终指向调用函数的对象。在这里，foo函数中，this指向的就是obj对象,因此在with(this)语句块里面,可以直接通过x变量来访问obj的x属性
	//【2】变量提升
	//js中的变量提升也是一个经常遇到的问题，我们可以简单理解成在js中，变量声明会被提升到函数的顶部，尽管有的时候，它是在后面声明的

所以上面的代码可以解析为:
	var obj = {
	    x: 10,
	    foo: function () {
	        var x;				//声明局部变量x
	        var y;				//声明局部变量y
	        with (obj) {
	            x = 20;			//访问变量x，在obj上找到x，则修改为20
	            y = 30;			//访问变量y，在obj上找不到y，则进一步查找，找到局部变量y，修改为30
	            console.log(y);	//打印 	30, 直接输出局部变量y，
	        }
	    }
	};
	obj.foo();
	console.log(obj.x);			//打印 	20, obj.x已被修改为20
	console.log(obj.y);			//打印 	undefined,	obj不存在y属性,则为undefined
</script>
最后一例：
<script type="text/javascript">
	({
	    x : 10,
	    foo: function () {
	    	console.log(this);			//第一步执行,打印{x: 10, foo: ƒ},由于整个大函数体的最外面是({...}).foo()所以是整个大函数体调用的foo函数,
	    								//所以this指向的是{x: 10, foo: ƒ}
	        function bar() {
	        	console.log(this);		//第四步执行,打印{x: 20, foo: ƒ}
	            console.log(x);			//第五步执行,打印undefined,因为bar函数内部并没有x变量,所以上foo函数作用域里找,此时foo函数作用域里x值为undefined
	            console.log(y);			//第六步执行,打印30,因为bar函数内部并没有y变量,所以上foo函数作用域里找,此时foo函数作用域里y值为30
	            console.log(this.x);	//第七步执行,打印20

	        }
	        //由于下面的with(this){...}语句里有
	        //{	...
	        //	var x = 20;
	        // 	var y = 30;
	        // 	...}
	        //所以相当于在这个位置系统隐式执行了
	        //	var x;
	        // 	var y;
	        //此位置所在的作用域为foo的作用域
	        with (this) {				//foo函数执行完之后会执行with(){}语句
	        	console.log(this);		//第二步执行,打印{x: 10, foo: ƒ},由于with(..){...}不是函数,所以打印的是传入的this,此时this依旧指向{x: 10, foo: ƒ}
	            var x = 20;				//相当于先在与with同级的作用域下var x(在这里就是foo函数内),
	            						//然后在this里找有没有this.x这个属性,发现有,于是给this.x赋值为20，
	            						//所以此时foo函数作用域内部已经有了x变量，但是值为undefined
	            var y = 30;				//同上,相当于先var y,然后在this里找有没有this.y这个属性,发现没有,于是这条语句相当于在foo函数内var y = 30
	            console.log(this);		//第三步执行,打印{x: 20, foo: ƒ}，这里的this为什么是{x: 20, foo: ƒ}:
	            						//执行到with (this) {var x = 20;var y = 30;}时会先在with同级作用域下声明新的局部变量,
	            						//然后在this里寻找有没有x以及y属性，如果有直接给其赋值，如果没有就赋值给新声明的变量
	            						//with(..){...}不是函数，他只是将代码的作用域设置到一个特定的作用域中
	            bar.call(this);			//调用this对象执行bar方法。注意:如果这里面没使用bar.call(this)而是直接使用bar(),
	            						//则第四步执行时会打印window{...},因为bar()只是执行一个名为bar的函数
	            console.log(x);			//第八步执行,打印20,在这里打印x是在this的作用域下打印x,在此作用域下x = 20 
	            console.log(y);			//第九步执行,打印30,在这里打印x是在this的作用域下打印y,在此作用域下y = 30 
	        }
	        console.log(x);				//第十步执行,打印undefined,在这里打印x是在foo的作用域下打印x,在此作用域下x值为undefined
	        console.log(y);				//第十一步执行,打印30,在这里打印y是在foo的作用域下打印y,在此作用域下y值为30
	    }
	}).foo();
</script>
总结:这里汇总了with语句的特点和弊端,总的来说,在日常编码中,我们只需要知道不去使用with就可以了
***************************
*                         *
*    throw、try 和 catch	  *
*                         *
***************************
	try 测试代码块的错误。
	catch 处理错误。
	throw 创建自定义错误。
一、try...catch用法:
<script type="text/javascript">
	try{
		//有可能出现错误的代码写在这里
	}catch(err){
		//在这里处理错误
	}finally{
		//【1】不管有没有出现异常,finally块中代码都会执行;
		//【2】当try和catch中有return时,finally仍然会执行;
		//【3】finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,
		//不管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的；
		//【4】finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
	}
	return ;
	//情况1:try{} catch(){} finally{} return;
    //      显然程序按顺序执行。
	//情况2:try{ return; } catch(){} finally{} return;
    //      第一步：程序执行try块中return之前（包括return语句中的表达式运算）代码;
    //      第二步：再执行finally块，最后执行try中return;
    //     	第三步：对于finally块之后的return,因为程序在try中已经return; 所以不再执行。
	//情况3:try{} catch(){ return; } finally{} return;
    //      第一步：程序先执行try，如果遇到异常执行catch块;
    //      第二步：有异常：则执行catch中return之前（包括return语句中的表达式运算）代码,
    //     		   再执行finally语句中全部代码,最后执行catch块中return; 对于finally块之后的return代码不再执行。            
    //     		   无异常：执行完try再finally再return;
	//情况4:try{ return; } catch(){} finally{ return; }
    //      第一步：程序执行try块中return之前（包括return语句中的表达式运算）代码;
    //      第二步：再执行finally块,因为finally块中有return所以提前退出。
	//情况5:try{} catch(){ return; } finally{ return; }
    //      第一步：程序执行catch块中return之前（包括return语句中的表达式运算）代码;
    //      第二步：再执行finally块,因为finally块中有return所以提前退出。
	//情况6:try{ return; } catch(){ return; } finally{ return; }
    //      第一步：程序执行try块中return之前（包括return语句中的表达式运算）代码;
    //      第二步：有异常：执行catch块中return之前（包括return语句中的表达式运算）代码;
    //                   则再执行finally块,因为finally块中有return所以提前退出。
    //      无异常：则再执行finally块,因为finally块中有return所以提前退出。

	//最终结论：	执行try或者catch中的return语句之前,如果finally存在的话,都会先执行finally语句。
    //     		如果finally中有return语句，那么程序就return了，所以finally中的return是一定会被return的，
    //     		编译器把finally中的return实现为一个warning。
</script>
二、Error.name的六种值对应的信息：
	【1】EvalError : eval()的使用与定义不一致
	【2】RangeError : 数值越界
  ※【3】ReferenceError : 非法或不能识别的引用数值
  ※【4】SyntaxError : 发生语法解析错误
	【5】TypeError : 操作数类型错误
	【6】URIError : URI处理函数使用不当
如果在try里面的代码一旦发生错误,则不会执行错误后的try里面的代码,但不抛出错误,然后会去执行catch里面的代码,如果try里没有错误则catch里的代码不会被执行,例：
<script type="text/javascript">
	try{
		console.log('a'); 						//打印 	a
		console.log(b);							//这里b未定义,直接使用会报错
		console.log('c');						//上条语句报错，所以这条语句不执行
	}catch(err){
		console.log(err.name+': '+err.message);	//打印 	ReferenceError: b is not defined
												//try里代码有错误,所以执行这句,e.name代表错误名,e.message代表错误信息
	}finally{
		console.log('d')						//打印 	d,这条语句照常执行
	}
</script>
throw:
<script type="text/javascript">
	function test(x){
		try{
		  if(x == "") throw "empty";
		  if(isNaN(x)) throw "not a number";
		  if(x > 10) throw "too high";
		  if(x < 5) throw "too low";
	  	}
		catch(err){
	  		console.log("Error: " + err + ".");		
	  	}	
	}
	test('');		//打印 Error: empty.
	test('a');		//打印 Error: not a number.
	test(1);		//打印 Error: too low.
	test(100);		//打印 Error: too high.
</script>
****************************
*                          *
*      es5严格/标准模式     *
*						   *
****************************
目前浏览器使用的标准是基于es3.0 + es5.0的新增方法的,但es5.0和es3.0有冲突部分
如果使用了es5.0严格模式，那么es3.0和es5.0产生冲突的部分就使用es5.0标准，否则使用es3.0标准

启用es5.0严格模式方法：
	使用'use strict'语句
两种用法：
【1】全局严格模式
【2】局部函数内严格模式

为什么要使用'use strict'语句作为启用严格模式的方法?
因为如果使用语句的话,他就是一行字符串,不会对不兼容严格模式的浏览器产生影响,比如如果使用函数作为启用方法的话,如果浏览器不兼容则会报错

严格模式下不支持with(){}、arguments.callee、func.caller语句;变量赋值前必须声明;严格模式下拒绝重复属性和参数;
局部this必须被赋值(比如Person.call(null/undefined)赋值什么就是什么),在非严格模式下this未被赋值就指向window,严格模式下未赋值就是undefined
严格模式下预编译时this不再指向window,而是没有指向(就是undefined),但严格模式下全局域里的this依然是window
<script type="text/javascript">
	'use strict'
	console.log(this);			//打印window
</script>
*********************************
*                               *
*      return与分号插入机制      	*
*								*
*********************************
分号插入机制:
JavaScript对代码不会很挑剔,当没有正确使用分号结束本语句时,他会自动补上,该行为在函数返回一个对象字面量并且开放的大括号位于接下来的一行时会导致问题
以return为例:
<script type="text/javascript">
	function func(){
		return 					//因为由于系统在return后隐式添加了分号,该行语句相当于return undefined;
		{						//所以并未访问到接下来的代码
			name : "Batman"
		};
	}
	var a = func();
	console.log(a);				//打印 	undefined
</script>
*********************************
*                               *
*     return fn()与fn()的区别    *
*								*
*********************************
<!-- 没有return 的话函数默认返回undefined 所以var result = fn(){ 没有return语句的代码 }相当于var result = undefined -->
<script type="text/javascript">
	var i = 0;
	function fn(){
		i++;
		if(i < 3){
			fn();
		}else{
			return i;
		}
	}
	var result = fn();		
	console.log(result);	//打印 undefined
</script>
想输出3:
<script type="text/javascript">
	var i = 0;
	function fn(){
		i++;
		if(i < 3){
			return fn();	//在fn前加上return是重点
		}else{
			return i;
		}
	}
	var result = fn();
	console.log(result);	//打印 3 
</script>
<script type="text/javascript">
	//并不是else{return i}没有执行:
	var i = 0;
	function fn(){
		i++;
		if(i < 3){
			console.log(i)	//打印 1		//打印 2
			fn();
		}else{
			console.log(i);	//打印 3 说明这一步执行了
			return i;		//因为上一步执行了,所以这里i = 3也return了
		}
	}
	var result = fn();
	console.log(result);	//打印 undefined
</script>
为什么这里加不加return区别会这么大？
这里的主要原因很简单，JavaScript的函数都是有默认返回值的，如果函数结尾不写return，会默认返回undefined，
这就是为什么在chrome的console控制台里，写代码经常下面会出现一行undefined的原因。
再仔细看看这个例子，当i从0自增到3之后，如果没有手动写return，会默认return undefined。
当加上了 return后，则会把fn函数执行的结果返回回来，得到3。
<script type="text/javascript">
	//没写return的代码执行时是这样的:
	var i = 0;
	function fn(){
		i++;
		if(i < 3){
			console.log(i);							//1
	//——————————————1——————————————//
			(function(){
				i++;
				if(i < 3){
					console.log(i);					//2
	//——————————————2——————————————//
					(function(){
						i++;						//此时i=3
						if(i < 3){					//不进入
							console.log(i);	
	//——————————————3———————————————//
							(function(){
								i++;
								if(i < 3){
									console.log(i);
									(function(){})();	//其实这里还应该是fn(),不过是什么已经无所谓了,反正也不执行
								}else{
									return i;
								}
								return undefined;
							})();
	//——————————————3——————————————//
						}else{
							return i;
						}
						return undefined;
					})();
	//——————————————2——————————————//
				}else{
					return i;
				}
				return undefined;
			})();
	//——————————————1——————————————//
		}else{
			return i;
		}
	}
	//简化如下:
	var i = 0;
	function fn(){
		i++;
		console.log(i);						//1
		(function(){
			i++;
			console.log(i);					//2
				(function(){
					i++;					//此时i=3
						(function(){
							i++;
							return i;
							return undefined;
						})();
					return undefined;
				})();
			return undefined;
		})();
	}
	//相当于如下情况:
	var i = 0;
	function fn(){
		(function(){		//fn执行后会立即执行里面的立即执行函数,但无论里面如何执行并没有把执行后的值返回来,所以相当于白执行
			return ++i;
		})()
	}
	var result = fn();		//输出undefined
	console.log(result);
	//只有这样才会输出1:
	var i = 0;
	function fn(){
		return (function(){	//fn执行后会立即执行里面的立即执行函数,但无论里面如何执行并没有把执行后的值返回来,所以相当于白执行
			return ++i;
		})()
	}
	var result = fn();		//输出undefined
	console.log(result);	
</script>
*********************************
*                               *
*     eval()与new Function()    *
*								*
*********************************
eval()函数可以将任意字符串当做一个JavaScript代码来执行,不推荐使用eval()函数
例:
<script type="text/javascript">
	//不推荐的方法:
	var property = 'name';
	console.log(eval('obj.' + property));
	//推荐的方法:
	var property = 'name';
	console.log(obj[property]);
</script>
使用new Function()构造函数和eval()比较类似,如果一定需要使用eval(),可以考录使用new Function()来替代eval()。
这样做的一个潜在好处是由于在new Function()中的代码将在局部函数空间中运行,因此代码中任何采用var定义的变量不会自动成为全局变量。
另一个避免自动成为全局变量的方法是将eval()调用封装到一个立即执行函数中。
例如下面代码中只有un这个变量仍然是一个全局变量,会影响到命名空间:
<script type="text/javascript">
	console.log(typeof un);								//打印 	undefined 	
	console.log(typeof deux);							//打印 	undefined
	console.log(typeof trios);							//打印 	undefined
	var jsstring = 'var un = 1; console.log(un);';
	eval(jsstring);										//打印 	1

	jsstring = 'var deux = 2; console.log(deux);';
	new Function(jsstring)();							//打印 	2

	jssting = 'var trios = 3; console.log(trios);';
	(function(){
		eval(jssting);									//打印 	3
	}());												
	console.log(typeof un);								//打印 	number
	console.log(un);									//打印 	1
	console.log(typeof deux);							//打印 	undefined
	console.log(typeof trios);							//打印 	undefined
</script>
另一个new Function()和eval()的区别在于eval()会影响到作用域链,执行eval()时相当于在原有作用域下直接执行eval括号内的语句;
而Function更多地类似于一个沙盒,无论在哪里执行Function,他都仅仅能看到全局作用域,而无法改变,
执行Function()时相当于在原有作用域下新嵌套了一层函数,然后在新函数内执行Function括号内的语句
因此对局部变量的影响比较小,在下例中,eval()可以访问和修改它外部作用域的变量,而Function不行(请注意使用Function和使用new Function是一样的)
<script type="text/javascript">
	(function(){
		var local = 1;
		eval('local = 3; console.log(local)');			//打印3
		console.log(local);								//打印3
	}());
</script>
<script type="text/javascript">
	(function(){
		var local = 1;
		Function('local = 3; console.log(local)')();	//打印3
		console.log(local); 							//打印1
	}());
</script>
<script type="text/javascript">
	(function(){
		var local = 1;
		eval('console.log(typeof local);');				//打印number
	}());	
</script>
<script type="text/javascript">
	(function(){
		var local = 1;									
		Function('console.log(typeof local);')();		//打印undefined
	}());
</script>
<script type="text/javascript">
	(function(){
		eval('console.log(typeof local);console.log(local)');		//打印undefined,	undefined
		var local = 1;
	}());	
</script>
<script type="text/javascript">
	(function(){
		Function('console.log(typeof local);console.log(local)')();	//打印undefined, 报错Uncaught ReferenceError: local is not defined
		var local = 1;
	}());
</script>
<script type="text/javascript">
	(function(){
		eval('console.log(typeof local);');				//打印undefined
	}());	
</script>
<script type="text/javascript">
	(function(){
		Function('console.log(typeof local);')();		//打印undefined
	}());
</script> 

异步编程
https://segmentfault.com/a/1190000017877701
*********************
*                   *
*       dom部分     	*
* 				    *
*********************
*                	*
*  Emmet插件使用方法	*
* 				    *
*********************
https://segmentfault.com/a/1190000007812543
一、嵌套操作符
【1】使用 > 生成元素子节点
例:div>ul>li:
	<div>
	    <ul>
	        <li></li>
	    </ul>
	</div>
【2】使用 + 生成元素兄弟节点
例:div+p+bq:
	<div></div>
	<p></p>
	<blockquote></blockquote>
【3】使用 ^ 在元素父节点生成新的元素节点,操作符 ^ 的作用和 > 刚好相反,用 > 可以在子级生成新的节点
例1:div+div>p>span+em:
	<div></div>
	<div>
	    <p><span></span><em></em></p>
	</div>
例2:div+div>p>span+em^bq:
	<div></div>
	<div>
	    <p><span></span><em></em></p>
	    <blockquote></blockquote>
	</div>
用n个 ^ ,就可以在第n父级生成新的节点
例3:div+div>p>span+em^^^bq:
	<div></div>
	<div>
	    <p><span></span><em></em></p>
	</div>
	<blockquote></blockquote>
【4】使用 * 生成多个相同元素
例:ul>li*5:
	<ul>
	    <li></li>
	    <li></li>
	    <li></li>
	    <li></li>
	    <li></li>
	</ul>
【5】圆括号 () 是Emmet的高级用法，用来实现比较复杂的DOM结构
例1:div>(header>ul>li*2>a)+footer>p:
	<div>
	    <header>
	        <ul>
	            <li><a href=""></a></li>
	            <li><a href=""></a></li>
	        </ul>
	    </header>
	    <footer>
	        <p></p>
	    </footer>
	</div>
还可以嵌套使用圆括号 ()
例2:(div>dl>(dt+dd)*3)+footer>p:
	<div>
	    <dl>
	        <dt></dt>
	        <dd></dd>
	        <dt></dt>
	        <dd></dd>
	        <dt></dt>
	        <dd></dd>
	    </dl>
	</div>
	<footer>
	    <p></p>
	</footer>
二、属性操作符
【1】ID 和 CLASS,Emmet给元素添加ID和CLASS的方法和CSS的语法类似
例:div#header+div.page+div#footer.class1.class2.class3:
	<div id="header"></div>
	<div class="page"></div>
	<div id="footer" class="class1 class2 class3"></div>
【2】自定义属性,使用[ ]标记来添加自定义属性
例:td[title="Hello world!" colspan=3]
	<td title="Hello world!" colspan="3"></td>
【3】元素编号,使用 $ 操作符可以对重复元素进行有序编号
例1:ul>li.item$*5:
	<ul>
	    <li class="item1"></li>
	    <li class="item2"></li>
	    <li class="item3"></li>
	    <li class="item4"></li>
	    <li class="item5"></li>
	</ul>
还可以用多个 $ 定义编号的格式
例2:ul>li.item$$$*5:
	<ul>
	    <li class="item001"></li>
	    <li class="item002"></li>
	    <li class="item003"></li>
	    <li class="item004"></li>
	    <li class="item005"></li>
	</ul>
【4】更灵活的编号方式,使用 @ 修饰符可以改变编号的格式
在 $ 后面添加 @- 可以改变编号顺序
例1:ul>li.item$@-*5:
	<ul>
	    <li class="item5"></li>
	    <li class="item4"></li>
	    <li class="item3"></li>
	    <li class="item2"></li>
	    <li class="item1"></li>
	</ul>
在 $ 后面添加 @N 可以改变编号基数
例2:ul>li.item$@3*5:
	<ul>
	    <li class="item3"></li>
	    <li class="item4"></li>
	    <li class="item5"></li>
	    <li class="item6"></li>
	    <li class="item7"></li>
	</ul>
还可以组合使用上面的修饰符
例3:ul>li.item$@-3*5:
	<ul>
	    <li class="item7"></li>
	    <li class="item6"></li>
	    <li class="item5"></li>
	    <li class="item4"></li>
	    <li class="item3"></li>
	</ul>
三、文本操作符
Emmet使用 Text:{} 给元素添加文本内容
例:a{Click me}:
	<a href="">Click me</a>
注意: 
{text} 在Emmet中是被当成单独的元素来解析的,但当和其他元素结合使用时会有特殊的含义
比如:a{click} 和 a>{click} 会输出相同的结果,
但a{click}+b{here} 和 a>{click}+b{here} 输出的结果则不一样:
	<!-- a{click}+b{here} -->
	<a href="">click</a>
	<b>here</b>
	<!-- a>{click}+b{here} -->
	<a href="">
		click
		<b>here</b>
	</a>
在 a>{click}+b{here} 中,<b>元素是<a>元素的子节点,
这个就是区别：当 {text} 直接写在元素后面时,并不会改变父元素的上下文。
下面是一个更复杂的案例:
p>{Click }+a{here}+{ to continue}:
	<p>Click <a href="">here</a> to continue</p>
而
p{Click }+a{here}+{ to continue}:
	<p>Click </p>
	<a href="">here</a> to continue
四、综合使用:
#page>div.logo+ul#navigation>li*5>a{Item $}
<div id="page">
    <div class="logo"></div>
    <ul id="navigation">
        <li><a href="">Item 1</a></li>
        <li><a href="">Item 2</a></li>
        <li><a href="">Item 3</a></li>
        <li><a href="">Item 4</a></li>
        <li><a href="">Item 5</a></li>
    </ul>
</div>
*************************
*				        *
*   Dom选择器、节点类型 	*
*                       *
*************************
一、Dom选择器
【1】Dom操作只是针对html和xml,无法针对css,对css的一切操作都是间接的,document代表整个文档,document是一个对象,上面有属性和方法
【2】html元素选择器(实时动态选择器)
【2.1】id选择器:	document.getElementById('') id与元素是一一对应关系
【2.2】标签选择器: document.getElementsByTagName('')	这是一个类数组
【2.3】name选择器: document.getElementsByName('') 由于name有自己的专有属性,表示的是能提交数据的组建的数据名,所以只有部分标签name可生效(表单form、表单元素各种input、img、iframe)
【2.4】类选择器:	document.getElementsByClassName('') 这是一个类数组

※※※【3】css选择器: document.querySelector与document.querySelectorAll; css选择器不是实时的,选出来的是副本,
想更改css选择器选择出来的元素就必须用css选择器选择出来的数组(使用.querySelectorAll选择的元素)或元素自己(使用.querySelector选择的元素)进行更改
【3.1】单个css选择器: document.querySelector(选出来的是一个而不是一组)
<html>
	<div>
		<strong></strong>
		<div>
			<span>
				<strong class="demo">123</strong>
			</span>
		</div>
	</div>
	<script type="text/javascript">
		var strong = document.querySelector('div>span strong.demo')
		//控制台输入strong 	会输出<strong class="demo">123</strong>
	</script>
</html>
【3.2】整组css选择器: document.querySelectorAll(选出来的是一组而不是一个)
<html>
	<div>
		<strong></strong>
		<div>
			<span>
				<strong class="demo">123</strong>
			</span>
		</div>
	</div>
	<script type="text/javascript">
		var strong = document.querySelectorAll('div>span strong.demo')
		//控制台输入strong 	会输出NodeList [strong.demo]
	</script>
</html>
二、节点的类型(后面的数字是nodeType的返回值)
以<body>
	0<div id="first">
		1
		<strong></strong>
		<div id="second">
			<span>
				<strong class="demo">123</strong>
			</span>
		</div>
	</div>
	2
	<div id="third"></div>
	<script type="text/javascript">
		var first = document.getElementsByTagName('div')[0];
	</script>
</body>为例：
三、遍历节点树(找一个节点的关系节点,包括所有类型的节点)
【1】.parentNode	父节点(一个),最顶端的parentNode为#document,document的父节点为null
【2】.childNodes 子节点们	    	    	控制台输入first.childNodes 	输出[text, strong, text, div#second, text]
【3】.firstChild	第一个子节点             	控制台输入first.firstChild 	输出"\n 1 \n"
【4】.lastChild 	最后一个子节点           	控制台输入first.lastChild 	输出#text
【5】.nextSibling 后一个兄弟节点         	控制台输入first.nextSibling 	输出"\n 2 \n"
【6】.previousSibling 前一个兄弟节点 		控制台输入first.previousSibling  输出"\n 0"
四、基于元素节点树的遍历(找一个节点的关系节点,仅遍历元素类型的节点)
【1】.parentElement	返回当前元素的父元素节点 	first.parentElement == first.parentNode == <body></body>
【2】.children 只返回当前元素的元素子节点 				控制台输入first.children 输出[strong, div#second]
【3】.childElementCount === .children.length(当前元素节点的子元素节点个数)                 first.childElementCount == first.children.length == 2
【4】.firstElementChild	返回的是第一个元素节点       	控制台输入first.firstElementChild 	输出<strong></strong>
【5】.lastElementChild 返回的是最后一个元素节点		控制台输入first.lastElementChild 	输出<div id="second"></div>
【6】.nextElementSibling / .previousElementSibling 	返回后一个/前一个兄弟元素节点
五、节点的4个属性和1个方法
【1】.nodeName 	元素的标签名,以大写形式表示,只能读不能更改				控制台输入first.nodeName 	输出"DIV"
【2】.nodeValue 	(只有文本节点Text或注释节点Commnet才有这个属性)Text节点或Comment节点的文本内容,可读写
※【3】.nodeType 该节点的类型,只读,每个节点都有.nodeType属性,nodeType属性有6个常用值：
(1)元素节点	--> 	1	            控制台输入first.nodeType 	输出1
(2)属性节点 	--> 	2
例:<div id="only" class="demo"></div>
这个div有两个属性节点,分别是id="only"和class="demo"
(3)文本节点 	--> 	3
(4)注释节点 	--> 	8
(5)document --> 	9
(6)DocumentFragment 10

自己封装一个方法,把该元素所有元素子节点挑出来,返回成一个类数组的方法:
<script type="text/javascript">
	function retElementChild(node){
		var temp = {
				length : 0,
				push : Array.prototype.push,
				splice : Array.prototype.splice
			},
			child = node.childNodes,
			len = child.length;
		for(var i = 0; i < len; i++){
			if(child[i].nodeType === 1){
				temp.push(child[i]);
			}
		}
		return temp;
	}
</script>
【4】.attributes 	Element节点的属性集合
例:	
<html>
	<div id="only" class="demo"></div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		//控制台输入div.attributes会显示:
		//NamedNodeMap {0: id, 1: class, id: id, class: class, length: 2}
	</script>
</html>
【5】节点的1个方法:
node.hasChildNodes(),该元素是否有子节点,只返回true或false
*******************************
*                             *
*  DOM里各节点方法的继承树(图)  *
*                             *
*******************************
Dom树结构:
Node:
【1】Document(文档) : HTMLDocument : document
【2】CharacterData : Text + Comment(注释)
【3】Element : HTMLElement : 
	HTMLHeadElement + HTMLBodyElement + HTMLTitleElement + HTMLParagraphElement + HTMLInputElement + HTMLableElement + ...
【4】Attr
document的构造函数是HTMLDocument,HTMLDocument的方法继承自Document
<script type="text/javascript">
	console.log(typeof(document)); 		//打印 	object
	console.log(typeof(HTMLDocument)); 	//打印 	function
	console.log(typeof(Document)); 		//打印 	function
	console.log(document.constructor === HTMLDocument); 		//打印 	true
	console.log(document.__proto__ === HTMLDocument.prototype); //打印 	true
	console.log(HTMLDocument.__proto__ === Document); 			//打印 	true
	console.log(Document.__proto__ === Node); 					//打印 	true
</script>
document继承自HTMLDocument.prototype,HTMLDocument.prototype继承自Document,
Document函数是系统内置函数,控制台输入Document 会输出function Document() { [native code] }
【1】getElementById方法定义在Document.prototype上,即在Element节点上不能使用
【2】getElementsByName方法定义在HTMLDocument.prototype上,即非html中的document不能使用(比如xml document,Element)
【3】getElementsByTagName方法定义在Document.prototype和Element.prototype上
例:只想选中div里的span:
	<div>
		<span></span>
	</div>
	<span></span>
<script type="text/javascript">
	var div = documnet.getElementsByTagName('div')[0];
	var span = div.getElementsByTagName('span')[0];				//这样就可以只选中div里的span
</script>
【4】HTMLDocument.prototype定义了一些常用的属性,body、head 分别指代HTML文档中的<body><head>标签
【5】Documnet.prototype上定义了documentElement属性,指代文档的根元素,在HTML文档中,他总是指代<html>元素
【6】getElementsByClassName、querySelectorAll、querySelector在Document.prototype,Element.prototype类中均有定义
******************************
*                            *
*        几个练习题           *
*                            *
******************************
【1】遍历元素节点树(在原型链上编程)
<script type="text/javascript">
	Element.prototype.allChildren = function(){
		var children = this.children,
			len = children.length;
		for (var i = 0; i < len; i++){(children[i].children) ? children[i].allChildren() : this.appendChild(children[i])}
		console.log(this);
	}
</script>
【2】封装函数,返回元素e的第n层祖先元素节点
<script type="text/javascript">
	function retParent(elem, n){
		var elem = elem.parentElement;
		while(elem && --n){	//写while(elem && n)是为了容错,比如传入的element没有第10层祖先,而传入了n=10,则到null时就不执行while循环了
			elem = elem.parentElement; 
		}
		console.log(elem);
	}
</script>
【3】封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己
<script type="text/javascript">
	function retSibling(element, n){
		while(element && n){	//写while(elem && n)是为了容错,只有当element不为null时,才执行while循环,为null时就不执行while循环了
								//当n为0时也不会执行while循环
			if(n > 0){
				if(element.nextElementSibling){
					element = element.nextElementSibling;					
				}else{			//写这段是考虑到了兼容性问题,如果系统不支持.nextElementSibling方法就调用.nextSibling方法
					element = element.nextSibling;
					while(element && element.nodeType != 1){
						element = element.nextSibling;
					}
				}
				n --;
			}else{
				if(element.previousElementSibling){
					element = element.previousElementSibling;
				}else{
					element = element.nextSibling;
					while(element && element.nodeType != 1){
						element = element.previousSibling;
					}
				}
				n ++;
			}
		}
		return element;
	}
</script>
【4】编辑函数,封装myChildren功能,解决以前部分浏览器的兼容性问题
<script type="text/javascript">
	Element.prototype.myChildren = function(){
		var arr = [],
			child = this.childNodes,
			len = child.length;
		for(var i = 0; i < len; i++){
			if(child[i].nodeType == 1){
				arr.push(child[i]);
			}
		}
		return arr;
	}
</script>
【5】自己封装hasChildren()方法,不可用children属性
<script type="text/javascript">
	Element.prototype.myChildren = function(){
		var arr = [],
			child = this.childNodes,
			len = child.length;
		for(var i = 0; i < len; i++){
			if(child[i].nodeType == 1){
				return true;
			}
		}
		return false;
	}
</script>
*******************************
*                             *
*  DOM基本操作(节点的增删改查)  *
*                             *
*******************************
一、增
【1】document.createElement();	创建元素节点
【2】document.createTextnode();	创建文本节点
【3】document.createComment(); 	创建注释节点
【4】document.createDocumentFragment(); 	创建文本碎片节点
二、插
【1】parent.appendChild();		
任何元素节点都有appendChild操作,此操作属于剪切操作,前面的parent代表只有父级才能调用,操作与push类似,在元素最末尾添加
【2】parent.insertBefore(a, b);   
div.insertBefore(a, b)表示的是在div元素里insert a变量before b变量,也属于剪切操作,例:
 <div>
	<div>
		<div>
			<div></div>
			<span></span>
		</div>
	</div>
	<span></span>
</div>
 <script type="text/javascript">
 	var span = document.getElementsByTagName('span')[0];
 	var div0 = document.getElementsByTagName('div')[0];
 	var div1 = document.getElementsByTagName('div')[1];
 	div0.insertBefore(span, div1);
 </script>
 结构会变为:
  <div>
  	<span></span>
	<div>
		<div>
			<div></div>
		</div>
	</div>
	<span></span>
</div>
三、删
【1】parent.removeChild();
这个操作本质上是剪切,可以找一个变量接收他,然后留着以后再用
比如： 	var ii = div.removeChild(i); 则i元素在div里消失了,被剪切到ii里了,以后可以通过调用ii来调用i元素
【2】child.remove()
这个操作本质上是直接销毁
四、改(替换)
parent.replaceChild(new, origin);
*****************************
*                        	*
*   Element节点的属性、方法 	*
*                       	*
*****************************
一、Element节点的一些属性
【1】.innerHTML  	
改变元素里的HTML内容,可读可写,内容的结构为HTML结构
【2】.innerText(火狐不兼容) / textContent(老版本IE不好使)
改变元素里的文本内容
二、Element节点的一些方法
【1】ele.setAttribute()
改变元素里的属性值,例:div.setAttribute('id', 'only'),会把div元素里的id属性改为only
例:给所有的元素设置属性值为'this-name'的属性名:
<script type="text/javascript">
	var all = documnet.getElemnetByTageName('*');
	for(var i = 0; i < all.length; i++){
		all[i].setAttribute('this-name', all[i].nodeName);
	}
</script>
【2】ele.getAttribute() 	取属性的值
课堂小练习:
【1】请编写一段JavaScript脚本生成下面这段DOM结构。要求：使用标准的DOM方法或属性
<div class="example">
	<p class="slogn">最帅</p>
</div>
<script type="text/javascript">
	var div = document.createElement('div'),	//创建两个标签
		p = document.createElement('p'),
		text = document.createTextNode('最帅'); 	//创建文本节点

	div.setAttribute('class','example');		//更改两个标签的属性
	p.setAttribute('calss','slogn');

	p.appendChild(text);						//p标签后面插入文本节点
	div.appendChild(p);
	document.body.appendChild(div);		
</script>
【2】封装函数insertAfter();功能类似insertBefore(),提示:可忽略老版本浏览器,直接在Element.prototype上编程
<script type="text/javascript">
	Element.prototype.insertAfter = function(insertNode, originNode){
		var beforeNode = originNode.nextElementSibling;
		beforeNode ? this.insertBefore(insertNode, beforeNode) : this.appendChild(insertNode)
	}
</script>
【3】将目标节点内部的节点顺序逆序
eg:	<div><a id="1"></a><a id="2"></a><a id="3"></a><a id="4"></a><a id="5"></a><a id="6"></a></div>
	<div><a id="6"></a><a id="5"></a><a id="4"></a><a id="3"></a><a id="2"></a><a id="1"></a></div>
<script type="text/javascript">
	Element.prototype.reverse = function(){
		var children = this.children,		//取this里的所有元素节点做为children
			len = children.length;			//计算children长度
 		for(var i = len - 1; i >= 0; i--){	//从倒数第二位开始剪切下来,每次粘贴到this元素的最后面
 			this.appendChild(children[i])
 		}
 		return this
 	}
 	var div = document.getElementsByTagName('div')[0];
 	div.reverse();
</script>
【4】自己封装parent.unshiftChild()方法;方法类似于parent.appendChild(),在元素顶头添加
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0],
		p = document.createElement('p');
	Element.prototype.unshiftChild = function(){
		var children = this.children,			//获取this里的所有子元素
			len = children.length,				//获取子元素数组长度
			result = [];						//创造result为一个空数组
		result[0] = arguments[0];				//result数组第一位元素为需要插入的参数
		for(var i = 1; i < len + 1; i++){		//后面依次写入this里的子元素
			result[i] = children[i - 1];
		}
		return result;
	}
</script>
**********************
*				     *
*   date对象、定时器  *
*                    *
**********************
一、date对象
http://www.w3school.com.cn/jsref/jsref_obj_date.asp
【1】日期对象是系统提供好的
【2】时间戳
【3】求程序运行时间
<script type="text/javascript">
	var firstTime = new Date().getTime();
	for(var i = 0; i < 1000000000; i++){};
	var lastTime = new Date().getTime();
	console.log(lastTime - firstTime);
</script>
【4】封装函数,打印当前是何年何月何日何时,几分几秒
<script type="text/javascript">
	function nowTime(){
		var date = new Date(),
			year = date.getFullYear(),
			month = date.getMonth(),
			day = date.getDate(),
			hours = date.getHours(),
			minutes = date.getMinutes(),
			seconds = date.getSeconds(),
			xingqi = date.getDay();
		switch(xingqi){
			case 0:
				xq = '日';
				break;
			case 1:
				xq = '一';
				break;
			case 2:
				xq = '二';
				break;
			case 3:
				xq = '三';
				break;
			case 4:
				xq = '四';
				break;
			case 5:
				xq = '五';
				break;
			case 6:
				xq = '六';
				break;
		}
		console.log('现在是' + year + '年' + month + '月' + day + '日' + hours + '时' + minutes + '分' + seconds + '秒 星期' + xq);
	}
</script>
二、定时器
【1】定时器循环setInterval()、clearInterval();
语法:setInterval(function(){}, 1000);	
表示function(){}函数每隔1000毫秒自动执行一次,永无休止进行
注意,这里function(){}不需要起名字,起了也没有用,setInterval是window提供的方法,所以也可以写成window.setInterval(function(){}, 1000); 内部this指向window
<script type="text/javascript">
	var time = 1000;
	setInterval(function(){}, time);
	time = 2000;		//此时还是每隔1000毫秒执行一次,而不是2000毫秒
</script>
定时循环器不准:
<script type="text/javascript">
	var firstTime = new Date().getTime();
	setInterval(function(){
		var latTime = new Date().getTime();
		console.log(latTime - firstTime);
		firstTime = lastTime;
	}, 1000)
</script>
每个定时循环器都有一个返回值,返回值从1开始向上加,代表自己是第几个定时循环器,例:
<script type="text/javascript">
	timer = setInterval(function(){}, 1000);
	console.log(timer);		//打印1 	,代表第一个定时循环器
	timer = setInterval(function(){}, 1001);
	console.log(timer); 	//打印2 	,代表第二个定时循环器
	timer = setInterval(function(){}, 999);
	console.log(timer); 	//打印3 	,代表第三个定时循环器
</script>
停止定时循环器可以用clearInterval()方法
<script type="text/javascript">
	var i = 0;
	var timer = setInterval(function(){
		console.log(i++);
		if(i > 10){
			clearInterval(timer);
		}
	}, 10)					//系统每隔10毫秒打印一次i,i > 10时停止定时器
</script>
【2】定时器setTimeout()、clearTimeout()
语法:setTimeout(function(){}, 1000); 	
表示推迟1000毫秒后执行,只执行一次,方法与setInterval()类似
*********************
*                   *
*     BOM基本操作   	*
*                   *
*********************
【1】查看滚动条的滚动距离
window.pageXOffset / pageYOffset
IE8及IE8以下不兼容
document.body / documentElement.scrollLeft / scrollTop
兼容性比较混乱，用时取两个值相加，因为不可能存在两个同时有值
自己封装兼容性方法，g求滚动轮滚动距离getScrollOffset()
<script type="text/javascript">
	function getScrollOffset() {
	    if (window.pageXOffset) {
	        return {
	            x: window.pageXOffset,
	            y: window.pageYOffset
	        }
	    }
	    return {
	        x: document.documentElement.scrollLeft + document.body.scrollLeft,
	        y: document.documentElement.scrollTop + document.body.scrollTop
	    }
	}
</script>
【2】查看视口的尺寸
window.innerWidth/innerHeight （加上滚动条宽度 / 高度）
IE8及IE8以下不兼容
document.documentElement.clientWidth/clientHeight
标准模式下，任意浏览器都兼容
document.body.clientWidth/clientHeight
适用于怪异模式下的浏览器
自己封装兼容性方法，返回浏览器视口尺寸getViewportOffset()
<script type="text/javascript">
	function getViewportOffset() {
	    if (window.innerWidth) {
	        return {
	            w: window.innerWidth,
	            h: window.innerHeight
	        }
	    }
	    if (document.compatMode == "CSS1Compat") {			//标准模式下
	        return {
	            w: document.documentElement.clientWidth,
	            h: document.documentElement.clientHeight
	        }
	    } else if (document.compatMode == "BackCompat") {	//怪异模式下
	        return {
	            w: document.body.clientWidth,
	            h: document.body.clientHeight
	        }
	    }
	}
</script>
【3】查看元素的几何尺寸
domEle.getBoundingClientRect();
兼容性很好,该方法返回一个对象，对象里面有left,top,right,bottom等属性。left和top代表该元素左上角的X和Y坐标，right和bottom代表元素右下角的X和Y坐标
height和width属性老版本IE并未实现
返回的结果并不是“实时的”
自己封装兼容方法，查看元素的几何尺寸(返回宽高)
<script type="text/javascript">
	function getElementOffset(ele) {
	    var box = ele.getBoundingClientRect();
	    var w = box.width || (box.right - box.left);
	    var h = box.height || (box.bottom - box.top);
	    return {
	        w: w,
	        h: h
	    }
	}
</script>
【4】查看元素的尺寸
dom.offsetWidth，dom.offsetHeight
【5】查看元素的位置
【5.1】dom.offsetLeft, dom.offsetTop
对于无定位父级的元素，返回相对文档的坐标。对于有定位父级的元素，返回相对于最近的有定位的父级的坐标。
【5.2】dom.offsetParent
返回最近的有定位的父级，如无，返回body, body.offsetParent 返回null
eg：求元素相对于文档的坐标 getElementPosition()
<script type="text/javascript">
	function getElementPostion(ele) {
	    var x = 0,
	        y = 0;
	    while (ele != document.body) {
	        x += ele.offsetLeft;
	        y += ele.offsetTop;
	        ele = ele.offsetParent;
	    }
	    return {
	        x: x,
	        y: y
	    }
	}
</script>
【6】让滚动条滚动
window上有三个方法
scroll()、scrollTo()、scrollBy();
三个方法功能类似，用法都是将x,y坐标传入。即实现让滚动轮滚动到当前位置。
区别：scrollBy()会在之前的数据基础之上做累加。
利用scrollBy() 实现快速阅读的功能
<script type="text/javascript">
	var timer = 0,
		key = true; 				//先让key = true
	start.onclick = function(){ 	//start被点击,执行函数
		if(key){					//key = true,可以执行if里的语句
			timer = setInterval(function(){ //执行setInterval函数,把返回值赋给timer
				window.scrollBy(0, 10);
			}, 100);
		}
		key = false;  				//此时key变为false,再次点击时不会执行if里的语句,
									//这样只要点击一次start,start便被锁死,多次点击无效果
	}
	stop.onclick = function(){
		clearInterval(timer);		//timer计时循环器被清除
		key = true; 				//key = true,点击stop后key = true,start被解锁,再次点击有效
	}
</script>
【7】查看元素距离左右边框的距离：
<script type="text/javascript">
	function getElementPosition(ele){
		if(ele.offsetParent.nodeName == 'BODY'){
			return {
				l: ele.offsetLeft,
				t: ele.offsetTop
			}
		}else{
			return {
				l: ele.offsetLeft + getElementPosition(ele.offsetParent).l,
				t: ele.offsetTop + getElementPosition(ele.offsetParent).t
			}
		}
	}
</script>
*****************
*               *
*    脚本化css 	*
*               *
*****************
※※只有dom.style[prop]属性可以往里写值,其余方法均不可以,其余方法只能读
【1】dom.style.prop属性可读可写元素的行间样式(可间接操作css属性),任何DOM元素都有这个属性,把这个div所有能用的css都展示出来,不管是不是已经设置了
没有兼容性问题，碰到float这样的关键字属性，前面应加css,(eg:float — > cssFloat)
复合属性最好拆解(比如尽量不要直接写dom.style.border,而是要写dom.style.borderWidth、dom.style.borderStyle、dom.style.borderColor)
组合单词变成小驼峰式写法(eg: div.style.background-color - > div.style.backgroundColor)
写入的值必须是字符串格式,例：
<html>
	<div style="width: 100px; height: 100px;background-color: red"></div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		div.onclick = function(){
			div.style['backgroundColor'] = 'green';			//本来是红的,点击一下会变绿
		}
	</script>
</html>
【2.1】查询计算样式
window.getComputedStyle(ele, null) 	返回当前元素所展示的一切css显示值,包括默认值,返回的值是计算样式都是绝对值，没有相对单位
IE8 及 IE8以下不兼容
【2.2】查询样式
ele.currentStyle 计算样式只读,返回的计算样式的值不是经过转换的绝对值,IE独有的属性
封装兼容性方法getStyle(obj,prop);
<script type="text/javascript">
	function getStyle(obj, styleProp) {
	    if (obj.currentStyle) {
	        return obj.currentStyle[styleProp];
	    } else {
	        return window.getComputedStyle(obj, null)[styleProp];
	    }
	}
</script>
获取伪元素div::after的宽度样式:
<script type="text/javascript">
	window.getComputedStyle(div,'after').width
</script>
*****************************
*                          	*
*   事件处理程序的绑定与解除  	*
*                 	       	*
*****************************
一、如何绑定事件处理程序
【1】ele.onxxx = function (event) {}
兼容性很好，但是一个元素只能绑定一个处理程序
基本等同于写在HTML行间上,例:
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.onclick = function(){
		console.log('a');
	}
</script>
【2】ele.addEventListener(type, fn, false);
IE9以下不兼容，可以为一个事件绑定多个函数,但是同一个函数被绑定多次只会执行一次,例:
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.addEventListener('click', function(event){}, false);
</script>
【3】ele.attachEvent('on' + type, fn);
IE独有，一个事件同样可以绑定多个处理程序,如果同一个函数被绑定多次也会执行多次,例:
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.attschEvent('onclick', function(event){});
</script>
小练习：使用原生js,addEventLinster,给每个li元素绑定一个click事件,输出他们的顺序
<html>
	<ul>
		<li>a</li>
		<li>a</li>
		<li>a</li>
		<li>a</li>
	</ul>
错误写法：
	<script type="text/javascript">
		var liCol = document.getElementsByTagName('li'),
			len = liCol.length;
		for (var i = 0; i < len; i++){
			liCol[i].addEventListener('click', function(){
				console.log(i)		//这样写会形成闭包:
			}, false);				//当执行for循环时只是单纯的把函数console.log(i)贴到了li[0-3]的click事件上,但执行这个事件却是要等到for循环结束后,
		}							//所以当for循环执行完后,每个li的click事件都变为执行console.log(i),而这里所有的i都指向同一个i,
									//当for循环结束后,这里的i已变成了4,所以不管点几都打印4。
									//解决办法就是使用立即执行函数,每执行一次for循环把这一次循环得到的i立即传给console.log(i),就能让i在执行完for循环后不同了
	</script>						
正确写法：
	<script type="text/javascript">
		var liCol = document.getElementsByTagName('li'),
			len = liCol.length;
		for(var i = 0;i < len; i++){							//模型是	for(var i = 0; i < len; i++){	
			(function(j){										//			(function0(i1){
				liCol[j].addEventListener('click', function(){	//				function1(i1){...}
					console.log(j)								//			}(i))
				}, false)										//		}
			}(i))												//这样就能在每次执行完1圈for循环后立即让function1调用参数i,function1是实际需要的函数
		}														//而function0实际没用只是作为立即执行函数存在
	</script>
</html>
二、如何解除事件处理程序(事件处理程序的接触形式要和绑定的形式一致才行)
【1】ele.onclick = false / '' / null;
【2】ele.removeEventListener(type, fn, false);
【3】ele.detachEvent('on' + type, fn);
注:解除【2】和【3】里的函数fn,必须是解除引用的函数,若绑定匿名函数,则无法解除
*************************************
*                                 	*
*   事件处理程序的运行环境(this指向)	*
*                          		  	*
*************************************
【1】ele.onxxx = function (event) {}
程序this指向是dom元素本身,例：
<html>
	<div style="width: 100px;height: 100px;background-color: red;"></div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		div.onclick = function(){
			console.log(this);		//打印<div style="width: 100px;height: 100px;background-color: red;"></div>
		}
	</script>
</html>
【2】obj.addEventListener(type, fn, false);
程序this指向是dom元素本身,例：
<html>
	<div style="width: 100px;height: 100px;background-color: red;"></div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		div.addEventListener('click', function(){
			console.log(this);
		}, false)					//打印<div style="width: 100px;height: 100px;background-color: red;"></div>
		
	</script>
</html>
【3】obj.attachEvent('on' + type, fn);
程序this指向window,例：
<html>
	<div style="width: 100px;height: 100px;background-color: red;"></div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		div.attachEvent('onclick', function(){
			console.log(this);		//打印[object Window],仅在ie10及ie10以下好使
		});
	</script>
</html>
如何使attachEvent函数的this能指向自己:
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.attachEvent('onclick', function(){
		handle.call(div);			//这里执行function函数后会调用handle函数,而调用handle的方式是改变了this指向,让div调用handle方法
	});
	function handle(){   			//这是实际的事件处理函数
		//事件处理程序
	}
</script>
自己封装兼容性的 addEvent(elem, type, handle)方法,可以实现给一个DOM对象(elem)的事件(type)添加一个事件处理函数(handle):
<script type="text/javascript">
	function addEvent(elem, type, handle){
		if(addEventListener){
			elem.addEventListener('type', handle, false);
		}else if(attachEvent){
			elem.attachEvent('on' + type, function(){
				handle.call(elem);	//这样写是为了考虑到有可能操作处理函数里的this,这样写this会永远指向elem
			})else{
				elem['on' + type] = handle;
			}
		}
	}
</script>
***********************************
*                           	  *
* 事件处理模型(事件冒泡、捕获)及取消 *
*                                 *
***********************************
attachEvent与addEventListener区别:
addEventListener需要传递三个参数，而attachEvent仅需要传递两个参数(缺少事件侦听参数)，这里牵扯到“事件流”的概念。
侦听器在侦听检查是否调用了监听函数时有三个阶段：捕获阶段、目标阶段和冒泡阶段。
顺序为：捕获阶段（根节点到子节点）→目标阶段（目标本身）→冒泡阶段（目标本身到根节点）。
此处的参数确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true，则侦听器只在捕获阶段处理事件，而不在目标或冒泡阶段处理事件。
如果useCapture 为 false，则侦听器只在目标或冒泡阶段处理事件。 
如果要在所有三个阶段都侦听事件，请调用两次 addEventListener，一次将 useCapture 设置为 true，第二次再将useCapture 设置为 false。 

一、两种事件处理模型:
【1】事件冒泡(elem.addEventListener('type', handle, false))：
结构上（非视觉上）嵌套关系的元素，会存在事件冒泡的功能，即同一事件，自子元素冒泡向父元素(自底向上)。
【2】事件捕获(elem.addEventListener('type', handle, true))：
结构上（非视觉上）嵌套关系的元素，会存在事件捕获的功能，即同一事件，自父元素捕获至子元素(事件源元素)(自顶向下)。
IE没有捕获事件
同一个对象的同一个事件类型,只能遵循一种事件模型,即要么冒泡,要么捕获,不会同时既存在冒泡,又存在捕获
【3】事件捕获div.setCapture(),把页面上所有发生的事件都算作div上;解除事件捕获div.releaseCapture()。(只有IE能用)
【4】触发顺序：先捕获，后冒泡
※※写在最里面子元素的事件模型既不是冒泡也不是捕获,点击时只是事件执行
<html>
<!-- 	例(以点击box为例)： -->
	<div class="wrapper" style="width: 300px;height: 300px;background-color: red">
		<div class="content" style="width: 200px;height: 200px;background-color: green">
			<div class="box" style="width: 100px;height: 100px;background-color: orange"></div>
		</div>
	</div>
	<script type="text/javascript">
		var wrapper = document.getElementsByClassName('wrapper')[0],
			content = document.getElementsByClassName('content')[0],
			box = document.getElementsByClassName('box')[0];
		wrapper.addEventListener('click',function(){
			console.log('wrapperBubble')	//事件处理模型：冒泡
		}, false);
		content.addEventListener('click',function(){
			console.log('contentBubble')	//事件处理模型：冒泡
		}, false);
		box.addEventListener('click',function(){
			console.log('boxBubble')		//由于是点击的box,所以事件模型既不是捕获也不是冒泡,只是事件执行
		}, false);
		wrapper.addEventListener('click',function(){
			console.log('wrapper')			//事件处理模型：捕获
		}, true);
		content.addEventListener('click',function(){
			console.log('content')			//事件处理模型：捕获
		}, true);
		box.addEventListener('click',function(){
			console.log('box')				//由于是点击的box,所以事件模型既不是捕获也不是冒泡,只是事件执行
		}, true);
		//点击box后：
		//打印	wrapper						先执行捕获事件模型	
		//		content						先执行捕获事件模型	
		//		boxBubble					事件执行,先绑定的先执行,后绑定的后执行
		//		box 						事件执行,先绑定的先执行,后绑定的后执行
		//		contentBubble				后执行冒泡事件模型
		//		wrapperBubble				后执行冒泡事件模型
	</script>
</html>
【4】focus，blur，change，submit，reset，select 等事件不冒泡

二、取消冒泡：
【1】W3C标准: event.stopPropagation();	event是在事件模型里传入的形参
但不支持ie9以下版本,例：
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.onclick = function(e){
		var event = e || window.event;
		event.stopPropagation();
	}
</script>
【2】event.cancelBubble = true;
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.onclick = function(e){
		var event = e || window.event;
		event.cancelBubble = true;
	}
</script>
【3】封装取消冒泡的函数stopBubble(event)
<script type="text/javascript">
封装：
	function stopBubble(e){
		var event = e || window.event;
		if(event.stopPropagation){
			event.stopPropagation();
		}else{
			event.cancelBubble = true;
		}
	}
使用:
	div.onclick = function(e){
		stopBubble(e);
	}
</script>

三、阻止默认事件:
默认事件 — 表单提交，a标签跳转，右键菜单等
【1】return false;  以对象属性的方式注册的事件才生效
例：(oncontextmenu是鼠标点击右键的事件)
<script type="text/javascript">
	document.oncontextmenu = function(){
		console.log('a');		//按鼠标右键打印a
		return false; 			//阻止鼠标点击右键的默认事件
	}
</script>
【2】event.preventDefault(); W3C标注，IE9以下不兼容
<script type="text/javascript">
	document.oncontextmenu = function(e){
		var event = e || window.event;
		console.log('a');		//按鼠标右键打印a
		event.preventDefault(); //阻止鼠标点击右键的默认事件
	}
</script>
【3】event.returnValue = false; 兼容IE
<script type="text/javascript">
	document.oncontextmenu = function(e){
		var event = e || window.event;
		console.log('a');		//按鼠标右键打印a
		event.returnValue = false; 	//阻止鼠标点击右键的默认事件
	}
</script>
封装阻止默认事件的函数 cancelHandler(event);
<script type="text/javascript">
封装:
	function cancelHandler(e){
		var event = e || window.event;
		if(event.preventDefault){
			event.preventDefault();
		}else{
			event.returnValue = false;
		}
	}
使用:
	document.oncontextmenu = function(e){
		console.log('a');
		cancelHandler(e);
		return false;
	}
</script>
*******************************
*                    	      *
* 事件对象、事件源对象与事件委托 *
*                             *
*******************************
【1】事件对象
当事件发生时会产生事件对象(事件对象是关于事件信息的对象),事件对象的作用是用来记录事件发生时一些相关的信息。
注意,事件对象只有在事件发生时才会产生,我们无法手动创建,并且事件对象只能在处理函数内部访问,处理函数结束后该对象自动销毁。
获取事件对象:	event || window.event 用于IE
兼容性方法,返回事件事件发生时一些相关的信息
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.onclick = function(e){
		var event = e || window.event;
		console.log(event);
	}
</script>
如下代码中,当onmousemove事件发生时,就会产生一个事件对象,就是程序中的event对象
我们只能在这个匿名函数中访问到这个event对象,在函数外面是访问不到的,同样我们也无法手动创建这个event对象
<script>
    document.onmousemove = function (e) {
        var event = e || window.event;
        var div = document.getElementById('div');
        div.innerHTML = "clientX: "+ event.clientX;
    }
 </script>
【2】事件源对象:
在事件中，当前操作的那个元素就是事件源(事件源对象是产生这个事件的元素对象)。比如网页元素中a标签和input都有onclick事件
当点击a发生onclick事件时，事件源就是a标签，当点击input发送onclic事件是，事件源就是input。
event.target 火狐独有的; event.srcElement Ie独有的; 这俩chrome都有
兼容性写法
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
	div.onclick = function(e){
		var event = e || window.event;
		var target = event.target || event.srcElement;
		console.log(target);
	}
</script>
【3】事件委托：
事件委托可以使事件方面性能得到优化,优点:
	性能 不需要循环所有的元素一个个绑定事件
	灵活 当有新的子元素时不需要重新绑定事件
在JavaScript代码当中，添加到页面中的事件越多，页面的性能也就越差,导致这一问题的原因主要有：			
	每个函数都是对象，都会占用内存,内存中对象越多，性能也就越差。
	必须事先指定所有事件处理程序而导致的DOM访问次数增加，会延迟整个页面的交互就绪时间。
	为了进行页面的性能优化，因此我们会采用两种方法，就是事件委托和事件处理程序的移除。		
事件委托：	
	什么时候使用事件委托，其实，简单来说，当一个页面事件处理程序比较多的时候，我们通常情况下会使用它。			
	事件委托主要利用了事件冒泡，只指定一个事件处理程序，就可以管理一个类型的所有事件。
例如：我们为整个一个页面制定一个onclick事件处理程序，此时我们不必为页面中每个可点击的元素单独设置事件处理程序
    事件委托：给元素的父级或者祖级，甚至页面绑定事件，然后利用事件冒泡的基本原理，通过事件目标对象进行检测，然后执行相关操作。
    其优势在于,大大减少了事件处理程序的数量,在页面中设置事件处理程序的时间就更少了(DOM引用减少,所需要的查找操作以及DOM引用也就更少了)。
	document（注：下面的例子没有绑定在document上，而是绑定到了父级的ul上，最为推荐的是绑定在document上）对象可以很快的访问到，
	而且可以在页面生命周期的任何时点上为它添加事件处理程序，并不需要等待DOMContentLoaded或者load事件。
	换句话说，只要可单击的元素在页面中呈现出来了，那么它就立刻具备了相应的功能。
	整个页面占用的内存空间会更少,从而提升了整体的性能。
移除事件处理程序				
	每当将一个事件处理程序指定给一个元素时，在运行中的浏览器代码与支持页面交互的JavaScript代码之间就会建立一个连接,连接数量也直接影响着页面的执行速度。
	所以，当内存中存在着过时的“空事件处理程序”的时候，就会造成Web应用程序的内存和性能问题。				
那么什么时候会造成“空事件处理程序”的出现呢？				
	文档中元素存在事件，通过一些DOM节点操作（removeChild、replaceChild等方法），移除了这个元素，但是DOM节点的事件没有被移除。
	innerHTML去替换页面中的某一部分，页面中原来的部分存在事件，没有移除。
	页面卸载引起的事件处理程序在内存中的滞留。
解决方法：				
	合理利用事件委托；
	在执行相关操作的时候，先移除掉事件，再移除DOM节点；
	在页面卸载之前，先通过onunload事件移除掉所有事件处理程序。
	利用事件冒泡，和事件源对象进行处理

eg:点击每个li元素,打印li元素里的内容:
<html>
	<ul>
		<li>1</li>
		<li>2</li>
		<li>3</li>
		<li>4</li>
		<li>5</li>
	</ul>
	<script type="text/javascript">
		var ul = document.getElementsByTagName('ul')[0];
		ul.onclick = function(e){
			var event = e || window.event 					//兼容性方法获取点击ul时的事件对象
			var target = event.target || event.srcElement;	//兼容性方法获取事件源对象
			console.log(target.innerText);
		}
		//这里边ul有多大,li加起来就有多大,li加起来的大小基本覆盖了ul的大小,触发的每个li事件,都会冒泡到ul上,
		//所以每次点击li都会触发ul.click事件,而触发ul.click事件会打印事件源对象的innerText,
		//所以这样写的好处是即使以后再在ul里添加li也同样可以执行打印li内容的操作
	</script>
</html>
*************
*           *
*  事件分类 	*
*           *
*************
一、鼠标事件
【1】click 					鼠标左键点击事件,click = mousedown + mouseup
【2】mousedown、mouseup		鼠标按下、抬起事件(包括鼠标右键)
【3】mousemove 				鼠标移动事件
【4】contextmenu 			点击右键产生菜单事件
【5】mouseover、mouseout		鼠标移入this元素事件、移除this元素事件(老版本)
【6】mouseenter、mouseleave 	鼠标移入this元素事件、移除this元素事件(HTML5版本)
	mouseenter与mouseover、mouseleave与mouseout区别:
		mouseover、mouseout支持事件冒泡,mouseleave、mouseenter不支持事件冒泡
		https://github.com/qianlongo/zepto-analysis/issues/1
【7】移动端没有mousedown、mouseup和mousemove,分别对应的是touchstart、touchend和touchmove
DOM3标准规定:click事件只能监听左键,只能通过mousedown 和 mouseup、event.button来判断鼠标右键
事件绑定处理函数与绑定顺序没关系,按触发事件的先后顺序打印:
mousedown > mouseup > click
<script type="text/javascript">
	document.addEventListener('click',function(){
		console.log('click');
	}, false);
	document.addEventListener('mousedown',function(){
		console.log('mousedown');
	}, false);
	document.addEventListener('mouseup',function(){
		console.log('mouseup')
	}, false);
	//点击左键依次打印	mousedown
	// 					mouseup
	// 					click
	//点击右键依次打印	mousedown
	// 					mouseup
</script>
【7】用event.button属性来区分鼠标的按键，0 - > 左键; 1 - > 中间滚轮; 2 - > 右键
<script type="text/javascript">
	document.addEventListener('mousedown',function(e){
		var event = e || window.event;
		if(event.button == 0){
			console.log('左键');
		} else if(event.button == 2){
			console.log('右键');
		}
	}, false)
</script>
【8】如何解决mousedown和click的冲突:
<script type="text/javascript">
	var firsttime = 0,
		lasttime = 0,
		key = false; 
	document.addEventListener('mousedown', function(){
		firsttime = new Date().getTime();
	}, false);
	document.addEventListener('mouseup', function(){
		lasttime = new Date().getTime();
		if(lasttime - firsttime < 300){
			key = true;
		}
	}, false);
	document.addEventListener('click', function(){
		if(key){
			console.log('click');
			key = false;
		}
	}, false);
</script>
二、键盘事件
【1】事件名称
keydown; keyup; keypress
【2】触发顺序：
keydown > keypress > keyup
【3】keydown和keypress的区别
【3.1】keydown 可以响应任意键盘按键,如果只是需要获取操作则可以使用keydown
但shift + a(大写a)和 a(小写a)无法区分,用event.which属性区分按键
【3.2】keypress只可以相应字符类键盘按键,可以区分大小写,如果需要获取字符,并区分大小写则可以使用keypress
keypress返回ASCII码,可以转换成相应字符,用event.charCode区分按键
打印按键:
<script type="text/javascript">
	document.addEventListener('keypress', function(e){
		var event = e || window.event;
		console.log(String.fromCharCode(event.charCode));
	}, false)
</script>
三、文本操作事件
【1】input 	元素获得用户输入事件
【2】focus 	元素获得焦点事件
【3】blur 	元素失去焦点事件
【4】change 	元素值被改变事件
<html>
	<input type="text" value="请输入用户名" style="color: #999; border: 2px solid black; height: 50px; line-height: 50px; width: 300px;" onfocus="if(this.value == '请输入用户名'){this.value = ''; this.style.color = '#424242'}" onblur="if(this.value ==''){this.value = '请输入用户名'; this.style.color = '#999'}">
</html>
四、窗体操作类(window上的事件)
【1】scroll 滚动条操作事件
<script type="text/javascript">
	window.onscroll = function(){
		console.log(window.pageXOffset + ' ' + window.pageYOffset);
	}
</script>
【2】load,等window把所有内容加载(下载)好后再加载函数
<html>
	<script type="text/javascript">
		window.onload = function(){
			var div = document.getElementsByTagName('div')[0];
			console.log(div);
		}
	</script>
	<div></div>
</html>
*********************
*        		   	*
*  浏览器的渲染引擎 	*
*                  	*
*********************
domTree + cssTree = randerTree
dom树的绘制完成代表dom节点的解析完毕,而不是dom节点的加载完毕,也就是只知道节点是什么就可以了,不需要加载里面的诸如'src'之类的属性(src属性的资源下载叫加载)
绘制dom树符合深度优先原则,而不是广度优先
【1】randerTree的重新排列叫重排(reflow)
触发重排的操作:
dom节点的删除,添加;尽量减少dom节点的更改,会影响效率,编程尽量避免重排
dom节点的宽高,字体宽高,位置变化,display none ---> block
获取dom节点的信息(offsetWidth offsetLeft)
【2】randerTree的重新绘制叫重绘(repaint)
比如背景颜色,背景图片,字体颜色
*****************
*             	*
*   异步加载js 	*
*           	*
*****************
js加载的缺点：js正常加载方式为同步加载(单线程加载),加载工具方法(辅助js包)没必要阻塞文档,过多js加载还会影响页面效率,
一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作。有些工具方法需要按需加载，用到再加载，不用不加载。
javascript 异步加载的三种方案(执行时也不阻塞页面):
【1】defer 异步加载,只有IE能用。但要等到dom文档全部解析完才会被执行,(页面解析完毕一定发生在页面加载完毕之前,因为还有下载图片之类的事)。
可以把js写在script标签里
<html> 	
	<script type="text/javascript" defer="defer">
		//系统读到这条js的时候不会阻断html和css的加载,这条js语句会和css和html并行加载
	</script>
</html>
【2】async(asynchronous的缩写) 异步加载,加载完这个脚本就执行,W3C标准方法。async只能加载外部脚本(比如'src = tools.js'),不能把js写在script标签里。
<html>
	<script type="text/javascript" src="tools.js" async="async"></script>
</html>
※※【3】自己封装loadScript函数,能让js文件插入到DOM中,加载完毕后执行回调函数callback
<script type="text/javascript">
	function loadScript(url, callback){
		var script = document.createElement('script'); 	//创建一个script标签
		//以下写法是先绑定事件然后再写 script.src = url;原因在于写完script.src = url;后系统便会去下载url所指的js代码,
		//如果下载的速度足够快就有可能会在绑定事件前就下载完了,而绑定的事件依靠js文件的从未加载到加载完成后状态的改变而触发,所以要避免这样的事发生
		//先绑定事件,后加载js文件,这样才能确保绑定的时间有效,防止加载速度过快导致绑定事件失效
		if(script.readyState){ 							//IE方法
			script.onreadystatechange = function(){ 	//在IE里script加载完后会有readyState的改变
				if(script.readyState == 'complete' || script.readyState == 'loaded'){
					callback();        					//执行callback函数
				}
			}
		}else{ 											//Safari/Chrome/firefox/opera方法										
			script.onload = function(){ 				//当script加载完后会触发load事件
				callback();  							//执行callback函数
			}
		}
		//后加载js文件
		script.type = 'test/script'; 					//设置script标签的type属性变为'test/script'
		script.src = url;		 						//让script标签的src属性变为url,当系统执行到这一句时,系统会将src里的东西下载下来然后赋给script标签
														//这样就预加载了地址为url里的东西,异步加载url里的js文件(js文件里声明了callback函数),但不会执行
		//最后把js文件插入到DOM中,使其可以被执行
		document.head.appendChild(script); 				//把script标签添加到head里,到这一步才执行script标签里的语句(也就是url指向的js文件)
														//如果没有这句话,仅有script.src = url语句,则js文件只是被下载下来了,但不可以被执行
	}
</script>
【3.1】如何使用刚才自己封装的loadScript函数,(url指向demo.js,demo.js里有test函数,想执行test函数):
错误写法：
<script type="text/javascript">
	function loadScript(url, callback){...}	//声明函数loadScript,系统读到这时不会读里面的代码,而是等执行函数loadScript时再去读
	loadScript('demo.js', test);  			//这样写不行是因为在执行函数loadScript时,系统会将'demo.js'、test作为实参一起同时传入函数loadScript中
	 										//而demo.js传入后需要等到下载完成后才有test函数,一起传入的话test函数一定是未定义,所以会报错
</script>
正确写法:
<script type="text/javascript">  			
	function loadScript(url, callback){...} //声明函数loadScript
	loadScript('demo.js', function(){ 		//这样写是把test函数的执行写到了匿名函数体里,这样就变成一个函数引用,系统读到函数引用时不会管里面写的是什么,
		test(); 							//而是等到执行时再去读
	}); 									//执行这个匿名函数体里的test函数经历了如下步骤:js文件加载事件绑定 -> js文件加载 -> js文件可以被执行 ->test执行
</script>
【3.2】与被调用的js互相配合(想调用demo.js的tools里的test函数):
<html>
	<script type="text/javascript">
		//URL地址为'./demo.js'
		var tools = {
			test : function(){
				console.log(a);
			},
			demo : function(){}
		}
	</script>
	<script type="text/javascript">
		function loadScript(url, callback){
			var script = document.createElement('script');
			if(script.readyState){
				script.onreadystatechange = function(){
					if(script.readyState == 'complete' || script.readyState == loaded){
						tools[callback]();
					}
				}
			}else{
				script.onload = function(){
					tools[callback]();
				}
			}
			script.type = 'test/script';
			script.src = url;
			document.head.appendChild(script);
		}
		loadScript('./demo.js', test);
	</script>
</html>
*********************************
*              		           	*
* js加载时间线（浏览器加载时间线)	*
*                              	*
*********************************
浏览器在刚刚运行页面时会初始化js功能,当初始化js之后(js开始发挥作用的这一刻开始,也就是从js出生的这一刻开始),记录了一系列浏览器做的事。
第一步、创建Document对象
【1】创建Document对象，开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState = 'loading'。
第一步、开始逐步解析文档
【2】遇到link外部css，创建线程加载(异步加载)，并继续解析文档。
【3】遇到script外部js，并且没有设置async、defer(异步加载推迟执行)，浏览器加载，并阻塞，等待js加载完成并执行该脚本，然后继续解析文档。
【4】遇到script外部js，并且设置有async、defer，浏览器创建线程加载，并继续解析文档。对于async属性的脚本，脚本加载完成后立即执行。（异步禁止使用document.write()）
	因为document.write()是把写到里面的东西当做html文档输出到页面里面去,当整个文档几乎全部解析完的时候,再调用document.write()会把之前的所有文档流清空,替换成自己的文档流,例:
<html>
	<div style="width: 100px;height: 100px;background-color: red;"></div>
	<script type="text/javascript">
		document.write('a'); 	//此时会产生一个100*100的红色div,然后在他下面输出a,
								//因为文档会先解析html然后解析css,解析完后,文档的randerTree几乎创建完毕,
								//这时还没有开始完全绘制文档,但文档中各元素的宽高已被预留出来,位置已经确定好了,然后会把'a'当做文档流输出到页面里
	</script>
</html>
但如果这样:
<html>
	<div style="width: 100px;height: 100px;background-color: red;"></div>
	<script type="text/javascript">
		window.onload = function(){
			document.write('a'); 	//此时会输出a,
									//因为window.onload会等到整个文档全部解析解析并加载完后再执行里面的语句,
									//这时document.write会把之前的所有文档流消除,然后输出自己的文档流,异步加载时会产生类似效果
		}
	</script>
</html>
【5】遇到img等，先正常解析dom结构，然后浏览器异步加载src，并继续解析文档。
第三步、DOMTree刚刚构建完成,文档解析完毕
【6】当文档解析完成，document.readyState = 'interactive'。
【7】文档解析完成后，所有设置有defer的脚本会按照顺序执行。（注意与async的不同,但同样禁止使用document.write()）;
【8】document对象触发DOMContentLoaded事件(DOMContentLoaded事件只有在用addEventListener绑定才有用,这个事件就是DOM解析完就被触发)
这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段,此时浏览器可以开始监听(识别)事件了
<script type="text/javascript">
	console.log(document.readyState);  			//第一步打印 loading
	document.onreadystatechange = function(){ 	
		console.log(document.readyState); 		//第三步打印 interactive
												//第四步打印 complete
	} 											
	document.addEventListener('DOMContentLoaded', function(){
		console.log('a') 						//第二步打印 a
	}, false);
</script>
DOMContentLoaded与window.onload区别：
	window.onload是等到文档全部加载完才执行,而DOMContentLoaded是等到文档解析完执行,DOMContentLoaded要比window.onload快(先执行)。
使用DOMContentLoaded可以把script标签写在head标签里:
<html>
	<head>
		<script type="text/javascript">
			document.addEventListener('DOMContentLoaded', function(){
				var div = document.getElementsByTagName('div')[0];
				console.log(div);    //打印 	<div style="width: 100px;height: 100px;background-color: red"></div>
			}, false)
		</script>
	</head>
	<div style="width: 100px;height: 100px;background-color: red"></div>
</html>
【9】当所有async的脚本加载完成并执行后、img等加载完成后，document.readyState = 'complete'，window对象触发load事件。
第四步、文档加载完毕
【10】从此，以异步响应方式处理用户输入、网络事件等。
<script type="text/javascript">
	console.log(document.readyState);  			//第一步打印 loading
	document.onreadystatechange = function(){ 	
		console.log(document.readyState); 		//第二步打印 interactive
	} 											//第三步打印 complete
</script>
**********************
*                    *
*      正则表达式     *
*                    *
**********************
http://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
一、正则表达式的创建方法
【1】字面量
<script type="text/javascript">
	var reg = /abc/;
	var str = 'abcd';
	//控制台输入 	reg.test(str) 	返回true;
	var reg = /abc/i; 					//忽略大小写
</script>
【2】构造函数创建 new RegExp(pattern, attributes);
1)参数pattern是一个字符串,指定了正则表达式的模式或其他正则表达式
2)参数attributes是一个可选的字符串,包含属性 "g"、"i" 和 "m"，分别用于指定全局匹配、区分大小写的匹配和多行匹配。
ECMAScript标准化之前,不支持m属性。如果pattern是正则表达式，而s不是字符串，则必须省略该参数。
<script type="text/javascript">
	var reg = new RegExp('abc','i');	//忽略大小写(ignoreCase)
	var str = 'abcd';
	//控制台输入 reg.test(str) 	输出true;
</script>
【2.1】在使用构造函数创建正则表达式时,可以将已有的正则表达式当做参数传入
构造函数创建正则表达式时写new与不写new的区别:
1)写new,新创建的正则表达式与传入的正则表达式参数在表现形式上一致,但实际是两个正则表达式:
<script type="text/javascript">
	var reg = /abc/m;
	var reg1 = new RegExp(reg);
	console.log(reg);		//打印 	/abc/m
	console.log(reg1);		//打印 	/abc/m
	reg1.abc = 'abc';
	console.log(reg.abc);	//打印 	undefined
	console.log(reg1.abc);	//打印 	abc
</script>
2)不写new,新创建的正则表达式与传入的正则表达式参数指向同一个正则表达式
<script type="text/javascript">
	var reg = /abc/m;
	var reg1 = RegExp(reg);
	console.log(reg);		//打印 	/abc/m
	console.log(reg1);		//打印 	/abc/m
	reg1.abc = 'abc';
	console.log(reg.abc);	//打印 	abc
	console.log(reg1.abc);	//打印 	abc
</script>
二、正则表达式修饰符
【1】i(ignoreCase) 	执行对大小写不敏感的匹配
【2】g(global) 		执行全局匹配（查找所有匹配而非在找到第一个匹配后停止）
<script type="text/javascript">
	var reg = /ab/;
	var reg1 = /ab/g;
	var str = 'ababab';
	//控制台输入str.match(reg) 	打印['ab'];
	//控制台输入str.match(reg1) 	打印["ab", "ab", "ab"];
</script>
【3】m(multiline) 	执行多行匹配
1)有m:
<script type="text/javascript">
	var reg = /^a/gm; 			//^a表示以a开头的a,g表示执行全局匹配,m表示执行多行匹配
	var str = 'abcde\na'; 		//\n表示换行符
	//控制台输入str.match(reg) 	打印["a", "a"];
</script>
2)没m:
<script type="text/javascript">
	var reg = /^a/g; 			//^a表示以a开头的a,g表示执行全局匹配
	var str = 'abcde\na'; 		//\n表示换行符
	//控制台输入str.match(reg) 	只打印["a"];第二行的'a'不被匹配
</script>
三、正则表达式使用方法:
1)reg.test() 	返回true/false,有没有符合规则的片段
2)str.match() 	把所有符合的片段返回,还能知道匹配了多少个
四、正则表达式表示方法:
方括号用于查找某个范围内的字符：
【1】[abc]	    		查找方括号之间的任何字符。
【2】[^abc]	        查找任何不在方括号之间的字符。
【3】[0-9]	            查找任何从 0 至 9 的数字。
【4】[a-z]	            查找任何从小写 a 到小写 z 的字符。
【5】[A-Z]	            查找任何从大写 A 到大写 Z 的字符。
【6】[A-z]           	查找任何从大写 A 到小写 z 的字符。
【7】(red|blue|green)	查找任何指定的选项。
<script type="text/javascript">
	var reg = /[ab][cd][d]/g; 			//表示第一位是a或b,第二位是c或d,第三位是d,全局查找
	var str = 'abcd';
	//控制台输入str.match(reg) 	输出["bcd"]
	var reg1 = /[0-9A-Za-z][cd][d]/g; 	//表示第一位是0-9或A-Z或a-z,第二位是c或d,第三位是d,全局查找
	var reg2 = /[0-9A-z][cd][d]/g; 		//和上面表示的一样
	//控制台输入str.match(reg1) 	输出["bcd"]
	//控制台输入str.match(reg2) 	输出["bcd"]	
	var reg3 = /[^a][^b]/g; 			//表示第一位非a,并且第二位非b,全局查找
	var str1 = 'ab1cd';
	//控制台输入str1.match(reg3) 输出(2) ["b1", "cd"]
	var reg4 = /(abc|bcd)[0-9]/g;		//表示'abc'加一位数字或'bcd'加一位数字
	var str2 = 'bcd2';
	//控制台输入str2.match(reg4) 输出["bcd2"]
</script>
五、元字符:
 	\w === [0-9A-z_]        匹配字母或数字或下划线或汉字 等价于 '[^A-Za-z0-9_]'
	\W === [^\w]
	\d === [0-9] 	        匹配数字
	\D === [^\d]
	\s === [\r\n\f\t\v ] 	匹配任意的空白符,注意\v后面的是空格,在正则表达式里空格就表示空格
	  	\r		            查找回车符
	  	\n	                查找换行符
	  	\f	                查找换页符
	  	\t	                查找制表符
	  	\v	                查找垂直制表符
	\S === [^\s]
	\b === 单词边界          匹配单词的开始或结束
	\B === 非单词边界
	.  === [^\r\n]          匹配除换行符以外的任意字符
	\uxxx                   查找以十六进制数xxxx规定的Unicode字符
	^                       匹配字符串的开始
	$                       匹配字符串的结束

	量词后面跟?		        表示非贪婪模式
	(?:pattern)             不记录子正则表达式的匹配结果
	a(?='')                 正向预查肯定符号,表示对后面边界的肯定匹配要求
	a(?!'')                 正向预查否定符号,表示对后面边界的否定匹配要求
   	                        javascript不支持反向预查
	/[\u4E00-\u9FA5\uf900-\ufa2d\w]/ 匹配汉字
 
<script type="text/javascript">
	var reg = /\Wcd2/g;
	var str = 'b*cd2';
	//控制台输入str.match(reg) 输出["*cd2"]
	var reg1 = /[\w\d]/g;
	var str1 = 'a';
	//控制台输入str1.match(reg1) 输出['a']
	var reg2 = /\bc/g;
	var str2 = 'abc def ghi'; 	//这里面包含了6个单词边界分别是a前面,c后面,d前面,f后面,g前面,i后面,
	//想匹配所有字符可以这样写:
	var reg3 = [\s\S] 			//这样表示匹配\s字符或\S字符,而\s与\S互为补集,所以可以表示全部字符
</script>
六、量词
	正则表达式匹配遵循贪婪原则(能匹配多就不匹配少),想取消贪婪匹配原则在量词的后面加?
【1】  n+		表示这个变量可以出现1次至无数次 	{1,infinity}
【2】  n*		表示这个变量可以出现0次至无数次 	{0,infinity}
<script type="text/javascript">
	var reg = /\w+/g, 	//表示\w可以出现1至无数次
	    reg1= /\w*/g; 	//表示\w可以出现0至无数次
	    reg2= /\d*/g; 	//表示\d可以出现0至无数次
	var str = 'abc';
	//控制台输入str.match(reg) 	输出['abc'],此时\w出现了3次,正则表达式匹配遵循贪婪原则(能匹配多就不匹配少,例如这里能匹配3个就不匹配2次或1次)
	//控制台输入str.match(reg1) 	输出["abc", ""],此时\w先出现了3次,后出现了0次(也就是就没出现\w)
	//控制台输入str.match(reg2) 	输出["", "", "", ""],4个空串分别出现在a前,b前,c前,c后(这4个点被称为光标的逻辑点)
</script>
【3】  n?		表示这个变量可以出现0次至1次 	{0,1}
【4】  n{x}   表示这个变量只可以出现x次		{x}
【5】  n{x,y}	表示这个变量可以出现x次至y次  	{x,y}
【6】  n{x,}	表示这个变量可以出现至少x次 	{x,infinity}
【7】  n$	    匹配任何以n为结尾的字符串
【8】  ^n	    匹配任何以n为开头的字符串
正向预查与非正向预查:
【9】  ?=n	    匹配任何其后紧接指定字符串n的字符串(这种匹配方式也叫正向预查或正向断言)
【10】 ?!n 	匹配任何其后没有紧接指定字符串n的字符串(这种匹配方式也叫非正向预查)
例:想匹配后面有b的a,但b不参与匹配,只参与限定：
<script type="text/javascript">
	var str = 'abaaaaa'; 	
	var reg = /a(?=b)/g;
</script>
检验一个字符串首尾是否含有数字:
<script type="text/javascript">
	var reg = /^\d|\d$/g;
	var str = '123abc';
</script>
检验一个字符串首尾都含有数字:
<script type="text/javascript">
	var reg = /^\d[\s\S]*\d$/g;
	var str = '123abc456';
</script>
七、RegExp 对象方法
reg.exec()	    检索字符串中指定的值,返回找到的值,并确定其位置
reg.lastIndex 	表示reg.exec()方法确定的位置,可以手动修改lastIndex值,如果不全局匹配,lastIndex就不会动,例:
<script type="text/javascript">
	var reg = /ab/g,
		reg1= /ab/;
	var str = 'ababab';
	console.log(reg1.exec(str)); 	//打印 	["ab", index: 0, input: "ababab"]
	console.log(reg1.exec(str)); 	//打印 	["ab", index: 0, input: "ababab"]

	console.log(reg.lastIndex); 	//打印 	0
	console.log(reg.exec(str)); 	//打印 	["ab", index: 0, input: "ababab"]
	console.log(reg.lastIndex); 	//打印 	2
	console.log(reg.exec(str)); 	//打印 	["ab", index: 2, input: "ababab"]
	console.log(reg.lastIndex); 	//打印 	4
	console.log(reg.exec(str)); 	//打印 	["ab", index: 4, input: "ababab"]
	console.log(reg.lastIndex); 	//打印 	6
	console.log(reg.exec(str)); 	//打印 	null
	console.log(reg.lastIndex); 	//打印 	0
	console.log(reg.exec(str)); 	//打印 	["ab", index: 0, input: "ababab"]
	console.log(reg.lastIndex); 	//打印 	2
</script>
八、特殊方法:
【1】反向引用子表达式内容:(想匹配形如aaaa,aabb这样的表达式)
<script type="text/javascript">
	var str = 'aaaa',
		str1= 'bbbb',
		str2= 'aabb';
	var reg = /(\w)\1\1\1/g, 		//\1表示反向引用第1个子表达式的内容,全局匹配
		reg1= /(\w)\1(\w)\2/g; 		//\1表示反向引用第1个子表达式的内容,\2表示反向引用第2个子表达式的内容,全局匹配
		reg2= /(\w)\1(\w)\2/; 		//\1表示反向引用第1个子表达式的内容,\2表示反向引用第2个子表达式的内容,不全局匹配
	console.log(str.match(reg)); 	//打印 	['aaaa']
	console.log(str1.match(reg)); 	//打印 	['bbbb']
	console.log(str2.match(reg1)); 	//打印 	['aabb']

	console.log(reg1.exec(str2)); 	//打印 	["aabb", "a", "b", index: 0, input: "aabb"],其中"a"与"b"分别表示第1个子表达式匹配的内容和第2个子表达式匹配的内容
	console.log(str2.match(reg2)); 	//打印 	["aabb", "a", "b", index: 0, input: "aabb"],当使用str.match(reg)方法进行匹配时,如果不全局匹配则返回的值和reg.exec(str)一样
</script>
【2】字符串有一个stringObject.replace(regexp/substr, replacement)方法,只把第一次找到的regexp/substr替换为replacement,例：
<script type="text/javascript">
	var str = 'aabb';
	console.log(str.replace('a','c'));		//打印 	'cabb';	只用c去替换第一次找到的'a'
</script>
还可以给replacement参数里传入方法:
<script type="text/javascript">
	var reg = /(\w)\1(\w)\2/g; 		 //先匹配形如aabb格式的表达式
	var str = 'aabb';
	console.log(str.replace(reg, function($, $1, $2){  	//打印 'bbaa'
		return $2 + $2 + $1 + $1	//第1个参数$是正则表达式匹配的结果,第2个参数是第1个子表达式匹配的内容,第3个参数是第2个子表达式匹配的内容
	}));
</script>
想把字符串里aabb的形式变为bbaa:
<script type="text/javascript">
	var reg = /(\w)\1(\w)\2/g; 		 //先匹配形如aabb格式的表达式
	var str = 'aabb';
	console.log(str.replace(reg,'$2$2$1$1')); //打印 'bbaa'; 这里面'$2'表示引用第2个子表达式内容,'$1'表示引用第1个字表达式内容
</script>
想把the-first-name变为小驼峰式写法:
<script type="text/javascript">
	var reg = /-(\w)/g; 			//先把需要变的内容选出来
	var str = 'the-first-name';
	//方法1
	console.log(str.replace(reg, function($, $1){
		return $1.toUpperCase();
	}))
	//方法2
	console.log(str.replace(reg, '$1'))
</script>
字符串去重:
<script type="text/javascript">
	var str = 'aaaaaaaaabbbbbccccccccc';
	var reg = /(\w)\1*/g; 					//(\w)表示要出现一位\w,\1表示反向引用第一个子表达式,*表示可以出现0-无数次
	console.log(str.replace(reg,'$1')); 	//打印abc
</script>
给数字隔三位加逗号：
<script type="text/javascript">
	//方法1
	function read(str){
		var reg = /(?=(\B)(\d{3})+$)/g; 
		//如果这样写： var reg = /(?=(\B)(\d{3})+$)/ 则不是全局匹配,会匹配符合条件的,距离末尾最远的空字符; 
		return (str.replace(reg,','));
	}
	//分析
	//需要匹配的是空字符,给匹配的空字符上加逗号,需要从后往前数所以是/$/
	//这个空字符的结尾要有3的整数倍个连3位的数字,所以是/(\d{3})+$/,其中(\d{3})表示连3位数字的组合,后面的+表示1到多个这样的组合
	//然后还不能是以单词边界开头的所以是/(\B)(\d{3})+$/
	//符合上面条件的空,所以是 /(?=(\B)(\d{3})+$)/

	//方法2
	var str = 10000000;
	function toThousands(num) {
	    var num = (num || 0).toString(), result = '';
	    while (num.length > 3) {
	        result = ',' + num.slice(-3) + result;
	        num = num.slice(0, num.length - 3);
	    }
	    if (num) { result = num + result; }
	    return result;
	}
</script>
正则表达式实例:
<script type="text/javascript">
	//验证价格:
	var reg = /^(?:\d+)(?:\.\d{0,2})*$/;
	//验证身份证:
	var reg = /^(?:\d{14,17})(?:\d|[xX])$/
	//匹配至少两行的字符串,每行都为非全空字符串:
	var reg = /.*[^ ].*[\n\r].*[^ ].*/   
	//验证日期格式(yyyy-mm-dd/yyyy/mm/dd):
	var reg = /^\d{4}(-|\/)\d{1,2}(-|\/)\d{1,2}$/;
	//匹配文件后缀名
	var reg = /\.\w+$/
	//匹配邮箱
	var reg = /^(?:\w)(?:[\w\.-]+)*@(?:[\w-]+)(?:\.[\w-]+){1,2}$/
	//匹配源代码中的链接
	var reg = /href\s*=\s*".+?"/;
	//匹配最后一个me(从右向左匹配me)
	var reg = /(?:me)(?!(?:.*(?:me).*))/;
	//匹配html闭合标签(包括自闭合<img />和双闭合<p></p>):
	var reg = /<([a-z]+)(\s+\w+\s*=\s*(".*"|'.*'))*(\s*\/|\s*>[\s\S]*?<\/\1)>/i;
	//匹配非channel标签下的title:
	var reg = /<title>[\s\S]*?<\/title>(?!([\s\S]*?<\/channel>))/gi
	//既有英文又有空格:
	var reg = /.*[ ].*[a-z].*|.*[a-z].*[ ].*/
	//匹配html属性
	var reg = /(\s+\w+)\s*=\s*([^"\s]+)(?=[^<>]*>)/ig
	//匹配文件名
	var reg = /.*[\\](?!(?:.*[\\].*))/g;
	var res = str.replace(reg, '');
	//匹配IP地址
	var reg = /((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))/g
	//只要整个正则匹配成功，无论子正则表达式是否匹配成功，括号都会捕捉:
	var reg = /(red)|(yellow)|(white)/;
	//会捕捉到三个结果，尽管实际上最多只可能有一个括号匹配成功
	//但是只要有一个匹配到了，两外两个也会记录空串
</script>
*****************
*               *
*    BOM核心    	*
*               *
*****************
BOM对象包含：
Window 		JavaScript 层级中的顶层对象，表示浏览器窗口。
Navigator 	包含客户端浏览器的信息。
History 	包含了浏览器窗口访问过的URL。
Location    包含了当前URL 的信息。
Screen      包含客户端显示屏的信息。
window对象是BOM的顶层(核心)对象,玩转BOM,就是玩转window的属性和方法
window对象它具有双重角色,既是通过js访问浏览器窗口的一个接口,又是一个全局对象,这意味着在网页中定义的任何对象,变量和函数,都是window的属性
mouseenter与mouseover、mouseleave与mouseout:
	mouseover、mouseout支持事件冒泡,mouseleave、mouseenter不支持事件冒泡
	https://github.com/qianlongo/zepto-analysis/issues/1
clientheight和clientwidth不包含滚动条宽度,window.innerheight和window.innerwidth包含滚动条宽度,表示可视区宽高
让屏幕出现滚动条:
<html>
	<body style="width: 3000px;height: 3000px;"></body>
</html>
URL解析:
资源定位器 			url:
协议   				https://
域名        			www.baidu.com (:443)默认端口号
路径        			/s
传给服务器的参数   	?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=nba&rsv_pq=ab59f2a40002b86b&rsvn&rsv_enter=1&rsv_sug3=1
锚点 				#dasdas

锚点的监听:
【1】根据锚点的信息来选择对应要展示的内容
【2】切换内容时会映射到锚点之中
【3】检测锚点修改,修改以后再映射内容

网页从输入网址到展现给自己所经历的步骤:
【1】url -> ip(DNS解析)
【2】tcp 三次握手
【3】js html css img解析与加载
【4】js时间线
【5】渲染过程 -> 渲染引擎
【6】tcp 四次挥手

css渲染模式:
<script type="text/javascript">
	console.log(document.compatMode);
</script>
让label标签与与input标签产生联系,把label的for属性设为input的id属性值
<html>
	<p>
		<label for="demo">username</label>
		<input type="text" id = 'demo'> //此时点击label会聚焦在input上
	</p>
	<script type="text/javascript">
		var label = document.getElementsByTagName('label')[0];
		var input = document.getElementById('demo');
		label.onclick = function(){
			console.log(this); 			//点击label会打印 	<label for="demo">username</label>
		}  								//  				<input type="text" id = 'demo'>
		input.onclick = function(){
			console.log(this); 			//点击input会打印 	<input type="text" id = 'demo'>
		}
	</script>
</html>

属性和特性：
属性是后加的,特性是天生的,特性属于属性的一种
	type value id class 是天生就有的叫特性
	shengao（身高） tizhong（体重）  天生没有,是自己添加的叫属性
特性对DOM对象和html元素来说是一一对应的映射关系,但属性不是
<html>
	<input type="text" id = 'demo' data='duyi' xingming='liyichuan'>
	<script type="text/javascript">
		var input = document.getElementById('demo');
		input.id = 'lyc';
		//操作特性:
		//控制台输入input 	会输出<input type="text" id = 'lyc' data='duyi' xingming='liyichuan'>
		//控制台输入input.value = 10,页面里的input元素内部也随即显示10,因为特性对DOM对象和html元素来说是一一对应的映射关系
		//控制台输入input.type = 'password',页面里的input元素内部也随即以密码形式显示
		//操作属性:
		console.log(input.data); 	//打印 undefined, 	data只是属性而不是特性,js不能与html标签形成一一映射关系,你是你的我是我的
									//在选取的DOM对象里(也就是input标签)确实没有data属性(因为自己是自己的,不形成映射关系),所以打印undefined
		input.data = 'duer'
		//控制台输入input.data 	打印"duer"
		//控制台输入input 	依然会输出<input type="text" id = 'lyc' data='duyi' xingming='liyichuan'>
		//对于非特性的属性来说,行间的就是行间的,js的就是js的,他俩完全隔离,对于特性的属性来说,行间的就是js的,js的就是行间的
	</script>
</html>	
使用setAttribute给行间样式加属性：
<html>
	<input type="text" id = 'demo' data='duyi' xingming='liyichuan'>
	<script type="text/javascript">
		var input = document.getElementById('demo');
		input.setAttribute('log', '211'); 	//想给input标签加log='211'的属性
		//控制台输入input 	会输出<input type="text" id = 'lyc' data='duyi' xingming='liyichuan' log='211'>
		//获取后添加的属性:
		//控制台输入input.log 	会输出undefined,虽然加到行间上去了但依然没有一一映射关系,需要用getAttribute取值
		//控制台输入input.getAttribute('log') 	会输出'211'
	</script>
</html>

懒加载原理:
【1】监控滑轮事件
【2】不断判断当前div的位置
【3】采取预加载
【4】把图片正式的添加到页面中去

文档碎片————虚拟DOM
<html>
	<div id="wrapper"></div>
	<script type="text/javascript">
		var f = document.createDocumentFragment();
		var ul = document.getElementById('wrapper');
		for(var i = 0; i < 10; i++){
			var newli = docuemnt.createElement('li');
			newli.innerHTML = i + '';
			f.appendChild(newli);
		}
		ul.appendChild(f);
	</script>
</html>
字符串拼接:
<html>
	<div id="wrapper"></div>
	<script type="text/javascript">
		var htmlStr = '';
		var ul = document.getElementById('wrapper');
		for(var i = 0; i < 10; i++){
			htmlStr += '<li>' + i + '</li>'
		}
		ul.innerHTML = htmlStr;
	</script>
</html>

自己封装getElementsByClassName(_className)方法(单一参数):
<html>
	<div class="wrapper" id="ow">
		<p class="    demo   box   active   ">
			<span class="demo      active   ">2</span>
			<span class="   demo   ">3</span>
		</p>
		<span class="  demodemo  ">4</span>
		<p class="   box   demo   ">
			<span class="   demo   ">5</span>
		</p>
	</div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		Element.prototype.getElementsByClassName = Document.prototype.getElementsByClassName = function(_className){
			var allDomArray = this.getElementsByTagName('*');
			var lastDomArray = [];			//最后结果要作为lastDomArray数组返回,这里是初始化数组
			function trimSpace(str){ 		//封装去空格函数,可以吧多个空格替换成1个空格
				var reg = /\s+/g;
				var newstrClass = str.replace(reg, ' ');
				return newstrClass;
			}
			for(var i = 0; i < allDomArray.length; i++){
				var lastStrClass = trimSpace(allDomArray[i].className).trim(); 	//这里使用字符串的trim()方法可以去除掉字符串首尾的空格
				var classArray = lastStrClass.split(' ');
				for(var j = 0; j < classArray.length; j++){
					if(classArray[j] == _className){
						lastDomArray.push(allDomArray[i]);
						break; 				//只要发现有一个类名是_className,就可以不继续再循环找了,所以要写break
					}
				}
			}
			return lastDomArray;
		}
	</script>
</html>
自己封装getElementsByClassName(_className1 _className2 ...)方法(多参数,中间以空格隔开):
<html>
	<div class="wrapper" id="ow">
		<p class="    demo   box   active   ">
			<span class="demo      active   ">2</span>
			<span class="   demo   ">3</span>
		</p>
		<span class="  demodemo  ">4</span>
		<p class="   box   demo   ">
			<span class="   demo   ">5</span>
		</p>
	</div>
	<script type="text/javascript">
		var div = document.getElementsByTagName('div')[0];
		Element.prototype.getElementsByClassName = Document.prototype.getElementsByClassName = function(_className){
			var allDomArray = this.getElementsByTagName('*');
			function trimSpace(str){ 								//去空格函数,可以吧多个空格替换成1个空格,但是无法剔除字符串首尾的空格
				var reg = /\s+/g;
				var newstrClass = str.replace(reg,' ');
				return newstrClass;
			}

			var parameterStr = trimSpace(_className).trim(); 		//给传入的多个参数做去空格处理
			var parameterArray = parameterStr.split(' '); 			//给传入的参数转化为数组
			var lastDomArray = [];									//最后结果要作为lastDomArray数组返回,这里是初始化数组

			for(var i = 0; i < allDomArray.length; i++){ 			//给被查找的allDomArray做循环遍历
				var lastStrClass = trimSpace(allDomArray[i].className).trim(); 	//这里使用字符串的trim()方法可以去除掉字符串首尾的空格
				var classArray = lastStrClass.split(' ');
				for(var j = 0,k = 0; j < classArray.length; j++){ 	//这里一定要初始化两个参数j和k,j做classArray的循环参数,k做parameterArray的循环参数
					while(parameterArray[k] == classArray[j]){ 		//内循环parameterArray先从头开始找(第0个开始),classArray开始遍历,
						k++; 										//只要找到两个相同的parameterArray和classArray元素,parameterArray就开始找下一个(k++)
						j = 0; 										//然后继续从classArray的头开始遍历
						if(k == parameterArray.length){ 			//如果k == parameterArray.length,说明parameterArray里的所有元素在classArray都有,可以push了
							lastDomArray.push(allDomArray[i]);
							j = classArray.length;					//找到后classArray也不用找了,j = classArray.length;会终止外循环classArray的遍历
							break;
						}
					}
				}
			}			
			return lastDomArray;
		}
	</script>
</html>
自己封装字符串的trim()方法:
<script type="text/javascript">
	String.prototype.qu = function(){
		var reg = /^[\s]+|[\s]+$/g;
		return this.replace(reg, '');
	}
</script>
*************************
*                   	*
*  ES5数组扩展方法应用	*
*                   	*
*************************
*      forEach遍历		*
*************************
forEach方法不改变原数组,没有返回值
一、forEach源码:
<script type="text/javascript">
	Array.prototype.myForEach = function(func){
		var len = this.length;
		for(var i = 0; i < len; i++){
			func(this[i], i, this)
		}
	}
</script>
二、forEach方法中的function回调有三个参数：第一个参数是遍历的数组内容，第二个参数是对应的数组索引，第三个参数是数组本身,因此：
<script type="text/javascript">
	[].forEach(function(value, index, array){});
	//等价于
	$.each([],function(index, value, array){})
</script>
<script type="text/javascript">
	var arr = [1,2,3,4],
		sum = 0;
	arr.forEach(function(value, index, array){
	    console.log(array[index] == value);	//打印 	4个true
	    sum += value; 
    });
	console.log(sum);    		//打印10
</script>
二、在forEach中无法给函数添加break或return,必须要循环遍历完整个数组:
<script type="text/javascript">
	var arr = [1,2,3,4];
	arr.forEach(function(value, index, array){
		if(index == 2){
			break; 				//报错:Illegal break statement
		} 
		console.log(value)
    });
</script>
在forEach中使用break相当于这样:
<script type="text/javascript">
	Array.prototype.myForEach = function(func){
		for(var i = 0; i < this.length; i++){
			func(this[i], i, this){
				break; 			//这个break并不能打断for循环
			}
		}
	}
</script>
四、forEach是ES5中操作数组的一种方法，主要功能是遍历数组，例如：
<script type="text/javascript">
	var arr = [1,2,3,4];
	arr.forEach(function(ele, index){
		alert(ele);
	});
 	//等价于：
	var arr = [1, 2, 3, 4];
	for (var k = 0, length = arr.length; k < length; k++) {
 		alert(array[k]);
	}
</script>
五、foreach方法不返回任何东西:
<script type="text/javascript">
	var arr = [1,2,3,4,5,6]
	var newArr = arr.forEach(function(ele, index){
		console.log(ele);
	})
	console.log(newArr); 	//输出undefined
</script>
*********************
*     filter筛选		*
*********************
filter方法不改变原数组,需要有变量接收
一、filter源码:
<script type="text/javascript">
	Array.prototype.myFilter = function(func){
		var arr = []
		for(var i = 0; i < this.length; i++){
			if(func(this[i], i, this)){
				arr.push(this[i]);
			}
		}
		return arr;
	}
</script>
二、filter方法会返回一个新的筛选后的数组,如果返回true就留下,返回false就不要,返回的是原数组的元素,例:
<script type="text/javascript">
	var arr = [1, 4, 7, 2, 5, 8, 3, 6, 9];
	var newArr = arr.filter(function(ele, index){
		if(ele % 2 == 0){
			return true;
		}else{
			return false;
		}
	})
	console.log(arr)		//会打印 	[1, 4, 7, 2, 5, 8, 3, 6, 9]
	console.log(newArr); 	//会打印 	[4, 2, 8, 6]
</script>
三、filter方法使用的是浅克隆,如果克隆引用值则引用原地址的引用值:
<script type="text/javascript">
	var arr = [[1,2],[1,3],[2,9],[1,4],[1,6]],
		newarr = [];
	newarr = arr.filter(function(ele, index){
		if(ele[0] == 1){
			return true;
		}else{
			return false;
		}
	});
	arr[0][0] = 2;						//改写了arr[0][0]
	console.log(newarr); 				//打印 	[[2,2],[1,3],[1,4],[1,6]]
	console.log(arr); 					//打印 	[[2,2],[1,3],[2,9],[1,4],[1,6]]
	console.log(newarr[0] === arr[0]);	//打印 	true
</script>
*********************
*   map循环遍历数组	*
*********************
map方法不改变原数组,需要有变量接收,map默认也是浅拷贝
<script type="text/javascript">
	[].map(function(value, index, array){})
</script>
一、源码:
<script type="text/javascript">
	Array.prototype.myMap = function(func){
		var newarr = [];
		for(var i = 0; i < this.length; i++){
			newarr.push(func(this[i], i));
		}
		return newarr;
	}
</script>
二、map即是"映射"的意思,map方法会返回一个新的映射出来的数组,但返回的不是原数组的元素,而是return的值,并且每执行一次就返回一次,例:
<script type="text/javascript">
	var arr = [1, 5, 9, 8, 7, 4, 2, 6, 8];
 	var newarr = arr.map(function(ele, index){
 		if(ele % 2 == 0){
 			ele *= 1;
 			return ele;
 		}else{
 			ele *= -1;
 			return ele;
 		}
 	})
 	console.log(arr); 		//会打印 	[1,5,9,8,7,4,2,6,8];
 	console.log(newarr); 	//会打印 	[-1,-5,-9,8,-7,4,2,6,8];
</script>
三、直接在map函数里返回ele是浅层拷贝,如果数组里有引用值则更改这个元素会改变原元素:
<script type="text/javascript">
	var arr = [[1, 2], 4, 7, 2, 5, 8, 3, 6, 9];
 	var newarr = arr.map(function(ele, index){
 		return ele;
 	})
 	newarr[0][1] = 9;
 	console.log(arr); 		//[[1,9],4,7,2,5,8,3,6,9]
 	console.log(newarr);	//[[1,9],4,7,2,5,8,3,6,9]
</script>
深克隆：
<script type="text/javascript">
	var a = {
		name: 'abc',
		habbit: {
			dance: 'good',
			sing: {
				jazz: 'good',
				pop: 'nice'
			}
		},
		age: [1,5,8,9]
	};
	var b = {};
	function deepClone(origin, target){
		var target = target || {},											//容错处理,防止忘传target
			toStr = Object.prototype.toString;
		if(origin != null){ 												//如果origin不是null就进入循环
			for(var prop in origin){										//遍历origin内部元素
	 			if(origin.hasOwnProperty(prop)){							//防止遍历到原型链上
	 				var src, copy = origin[prop];
					if(copy !== 'null' && typeof copy == 'object'){ 		//如果正在遍历的这个origin[prop]存在,并且是引用值,进行下面的判断
					src = toStr.call(copy) == "[boject Array]" ? [] : {};	//使target[prop]与origin[prop]保持数据类型一致
						target[prop] = deepClone1(copy, src);				//把正在遍历的这个origin[prop]作为克隆对象,把相同数据类型的空对象作为目标对象
																			//再次调用deepClone函数,进入递归,使用target[prop]接收
					}else{													//如果origin[prop]是原始值就直接赋给target[prop]
						target[prop] = copy;
					}								
				}
			}
		}
		return target; 			//因为要递归调用deepClone(copy, src),且递归中有一步是target[prop] = deepClone(src, copy);所以要返回本次递归产生的target
	}							//如果什么也不写函数执行后会默认返回undefined
	deepClone(b, a);
	console.log(b);
</script>
四、给map源码改为深克隆:
<script type="text/javascript">
	Array.prototype.myMap = function(func){
		var newarr = [];
		for(var i = 0; i < this.length; i++){
			if(this[i] && typeof this[i] == 'object'){
				var newObj = {};
				deepClone(this[i], newObj);
				newarr.push(func(newObj, i));
			}else{
				newarr.push(func(this[i], i));
			}
		}
		return newarr;
	}
</script>
*************************
*   reduce从左到右迭代	*
*************************
源码:
<script type="text/javascript">
	Array.prototype.myReduce = function(func, init){
		var previous = init,
			k = 0;
		if(init == undefined){								//如果初始化未定义
			previous = this[0];
			k = 1;
		}
		for(k; k < this.length; k++){
			previous = func(previous, this[k], k, this);	//第一次传入时是把previous = init; 或 previous = this[0];作为参数传入func,
															//然后把返回的值作为下次函数func的previous参数传入
		}
		return previous; 									//for循环结束后,把最后的previous返回出来
	}
</script>
使用方法:
<script type="text/javascript">
	arr.reduce(function(preValue, ele, index, array){})		//其中preValue表示上一次执行function回调函数时返回的值
</script>
例:
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	arr.reduce(function(preValue, ele, index){
		console.log(preValue);						//会依次打印10,1,2,3,4;而不是打印[10,1,2,3,4]
		return ele;
	}, 10) 											//传入的10是初始化参数,由于第1次执行function函数时没有上一次的返回值,所以用10当做初始值
	//	ele		return	preValue
	//	1		1		10
	//	2		2		1
	//	3		3		2
	//	4		4		3
	//	5		5		4
</script>
如果不传入初始值则遍历数组会从第2个ele开始,然后把第1个元素作为preValue
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	arr.reduce(function(preValue, ele, index){
		console.log(preValue);						//会依次打印1,2,3,4;而不是打印[1,2,3,4]
		return ele;
	})
	//	ele		return	preValue
	//	2		2		1
	//	3		3		2
	//	4		4		3
	//	5		5		4	
</script>
用reduce做累加器:
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	var value = arr.reduce(function(preValue, ele, index){
		return preValue + ele;
	});
	console.log(value); 		//打印15
	//	preValue	ele		return	
	//	1			2		1+2=3
	//	3			3		3+3=6
	//	6			4		6+4=10
	//	10			5		10+5=15
</script>
*****************************
*   reduceRight从右到左迭代	*
*****************************
源码:
<script type="text/javascript">
	Array.prototype.myReduceRight = function(func, init){
		var previous = init,
			len = this.length,
			k = len - 1;
		if(init == undefined){
			previous = this[len - 1];
			k = len - 2;
		}
		for(; k >= 0; k--){
			previous = func(previous, this[k], k, this);
		}
		return previous;
	}
</script>
使用方法:
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	var value = arr.reduceRight(function(preValue, ele, index){
		console.log(index); 	//会依次打印 	//3	//2	//1	//0;而不是打印[3,2,1,0]
		return 'aimee';
	})
	console.log(value); 		//打印	'aimee'
</script>
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	var value = arr.reduceRight(function(preValue, ele, index){
		console.log(preValue); 	//会依次打印 	//5 //3个undefined;因为每次执行完之后没有return值
		//没有return
	})
</script>
<script type="text/javascript">
	var arr = [1,2,3,4,5];
	var value = arr.reduceRight(function(preValue,ele,index){
		console.log(preValue); 	//会依次打印   	//5	//4	//3	//2;
		return ele; 			
	})
</script>
*********************
*	 				*
*		加锁			*
*					*
*********************
同一个按钮多次使用,希望循环达到不同目的,可以加锁:
<html>
	<button id="btn" style="width: 100px;height: 50px;background-color: green">btn</button>
	<div class="demo" style="width: 100px; height: 100px;background-color: red"></div>
	<script type="text/javascript">
		var oBtn = document.getElementById('btn');
		var oDiv = document.getElementsByTagName('div')[0];
		var flag = true;
		oBtn.addEventListener('click',function(){
			if(flag){
				flag = false;
				oDiv.style.height = '50px'; 
			}else{
				flag = true;
				oDiv.style.height = '100px';
			}
		}, false)
	</script>
</html>
*************************
*                       *
*  js原生方法传参的细节	*
*                       *
*************************
【1】slice()，接收两个参数，第一个为开始index（从0开始），第二个为结束的index(也是从0开始，但是不包括index本身，只到index-1)。
	返回值是截取的数组，原数组不变化。传第三个参数，没有作用。
【2】splice()，接收无数个参数，第一个为开始的index（从0开始），第二个为删除的元素的个数（0代表不删除，1代表删除一个...），
	第三个为添加的元素，第四个也为添加的元素.....，返回值是删除的元素组成的数组，如果删除了0个就返回空数组，
	原数组会被改变成被操作后的数组（这里需要注意一个问题,比如：arr =[1,2,3],arr.splice(1,0,4)，arr会变成[1,4,2,3]，添加的4是在2的前面插入）。
【3】forEach方法（除了数组对象之外，forEach 方法可由具有 length 属性且具有已按数字编制索引的属性名的任何对象使用。比如：{0 : "a", 1 : "b", length : 2}）
【4】对于数组中出现的每个元素,forEach方法都会调用callback函数一次(采用升序索引顺序),但不会为数组中缺少的元素调用回调函数([1,2,"",3],第三个选项不会执行callback)。
    arr.forEach(callbackfn, thisArg),只接受两个参数，一个是回调方法callbackfn，
   	一个是 callbackfn 函数中的 this 关键字可引用的对象。如果省略 thisArg，则 undefined 将用作 this 值。
    对于数组中的每个元素，forEach 都会调用callbackfn 函数一次。
这时你回答出来这些已经觉得自己还可以了，然后面试官会问你那callback的三个参数是什么？
回调函数的语法如下所示：
    function callback(value, index, array1)，value：数组元素的值。index：数组元素的数字索引。array1：包含该元素的数组对象。可以在回调方法中改变原数组。
	这个方法的作用就是：利用数组中的值做一些应用，比如：显示出来，累加等等。
	这里大家可以去区分一下$("div")，jQuery对象的each方法。很像，但是在回调方法调用时，传参不一样，请仔细查看。
    链接地址：http://www.cnblogs.com/chaojidan/p/4141413.html
【5】map方法，跟forEach方法差不多，唯一的区别是，arr.map(callback, thisArg)，有返回值，返回值是：对每个数组中的元素执行callback后，callback返回的值组成的数组。
	当然原数组也不会改变，除非你在callback(value, index, array1)中直接改变array1。
	这个方法的作用就是：把数组组装成其他的形式，比如：对数组中的每一项加1，或者平方等等。
	这里大家可以去区分下$("div")，jQuery对象的map方法，也很像，但是传参不一样，同时，回调方法调用时，传参也不一样。请仔细查看。
    链接地址：http://www.cnblogs.com/chaojidan/p/4141413.html
【6】indexOf方法：stringObject.indexOf(searchvalue, fromindex); 接收两个参数：searchElement：必需。 要在 array1 中定位的值。fromIndex：可选。 
	用于开始搜索的数组索引， 如果省略 fromIndex，则从索引 0 处开始搜索。 如果 fromIndex 大于或等于数组长度，则返回 -1。 
	如果 fromIndex 为负，则搜索从数组长度加上 fromIndex 的位置处开始。
	如果计算所得的索引小于 0，则搜索整个数组。数组元素将与 searchElement 值进行全等比较，与 === 运算符类似。
【7】lastIndexOf方法 : stringObject.lastIndexOf(searchvalue, fromindex); 接收两个参数：searchElement：必需。 要在 array1 中定位的值。fromIndex：可选。 
	用于开始搜索的数组索引, 如果省略 fromIndex，则搜索将从数组中的最后一个索引处开始。 如果 fromIndex 大于或等于数组长度，则搜索整个数组。 
	如果 fromIndex 为负，则搜索从数组长度加上 fromIndex 的位置处开始。 
	如果计算所得的索引小于 0，则返回 -1。数组元素将与 searchElement 值进行全等比较，与 === 运算符类似。
【8】replace方法：它会跟正则一起出题。它接收两个参数，比如：str.replace(str1, str2);
	str1必需。规定子字符串或要替换的模式的 RegExp 对象。请注意，如果该值是一个字符串，则只会替换一个。比如:"abbc".replace("b","d"); 会返回"adbc"。
	str2必需。规定了替换文本或生成替换文本的函数。返回值，就是替换之后的字符串。原字符串不改变。
	如果需要把匹配到的字符全部替换，需要使用正则"abbc".replace(/b/g, "d"); 会返回"addc"。
	str2可以是函数。在这种情况下，每个匹配都调用该函数，它返回的字符串将作为替换文本使用。
	该函数的第一个参数是匹配模式的字符串。接下来的参数是与模式中的子表达式匹配的字符串，可以有 0 个或多个这样的参数。
	接下来的参数是一个整数，声明了匹配在 str中出现的位置。最后一个参数是 str本身。	比如：
<script type="text/javascript">
	"abcd".replace(/(a)(b)(c)/g, function(match, $1, $2, $3, index, str){　　
		//match : abc(匹配到的字符串),
		//$1 : a(匹配到的第一个子表达式),以此类推,如果没有子表达式,就不会有这些参数,就直接是index,
		//index : 0(匹配到的字符串在原始字符串中的位置,也就是"abc"在"abcd"中的位置),
		//str : "abcd"(原始字符串)
	})
</script>
【9】insertBefore方法:举个例子:
<ul id="ul"><li>1</li><li>2</li></ul>
<script type="text/javascript">
	var list = document.getElementById("ul");
</script>
list.insertBefore(newItem, list.childNodes[0]);
第一个参数必选,插入的新节点。第二个参数可选,在其之前插入新节点。如果未规定,则 insertBefore 方法会在list结尾插入 newnode。
*****************
*              	*
*  http缓存机制 	*
*            	*
*****************
http://www.cnblogs.com/chenqf/p/6386163.html
http://www.codeceo.com/article/broswer-http-cache.html#0-tsina-1-75257-397232819ff9a47a7b7e80a40613cfe1
与缓存相关的规则信息，均包含在报文的header中，对于强制缓存来说，响应header中会有两个字段来标明失效规则,
Cache-Control 是最重要的规则。常见的取值有private、public、no-cache、max-age，no-store，默认为private。
参数					打开新窗口时 					原窗口在地址栏重新enter时 	刷新 	   		点击返回按钮时
max-age=xxx         缓存的内容将在 xxx 秒后失效   缓存xxx秒                   	重新请求资源     	缓存xxx秒
no-cache            需要使用对比缓存来验证缓存数据	重新请求资源                 重新请求资源     缓存资源
no-store            所有内容都不会缓存，强制缓存，对比缓存都不会触发
public              客户端和代理服务器都可缓存    缓存资源                    	重新请求资源     	缓存资源
private             客户端可以缓存     	        第一次发请求，之后缓存资源    	重新请求资源     	缓存资源
must-revalidation   浏览器重新请求   	            第一次发请求，之后缓存资源    	重新请求资源     缓存资源

GET与POST的真正区别
提及GET和POST的区别，一定要确定基于什么前提。在不同的前提下有不同的答案。
在本质上，GET请求和POST请求都能拉取数据。
我们常说的HTTP协议实际上是基于RFC规范的，实际上GET和POST请求的语法是完全相同的，但是在RFC规范中，给GET请求和POST请求规定了语义，规定GET用来获取信息，POST用来发送信息。
所以我们最后来总结一下：
当人们问起GET和POST的区别时，我们要先确定，这里的GET和POST是基于什么前提的？
1. 如果什么前提都没有，也就是不用任何规范限制的话，我们只考虑语法来说，这两个方式是没有任何区别的，只有名字不一样。
2. 如果是基于RFC规范的，那么问题就又来了。是基于RFC理论的，还是基于具体的实现的。
（1）如果是基于RFC理论的，我们称这个为Specification。那么GET和POST是具有相同的语法，但是不具备相同的语义，GET方式用作获取信息，POST方式用作发送信息。
（2）如果是基于RFC的具体实现的，我们称之为implementation。其实要区分是具体的哪一种实现。我们通常默认指的是浏览器实现的RFC。
	当然不止浏览器，我们任何人都可以设计一个HTTP协议的接口，使用RFC规范，当然这些是我们不用考虑的，因为并不通用。
所以我们只需要考虑浏览器实现的RFC，或者说Web环境下的RFC。这个前提下的答案，就是我们最常见的那些。我就简单的列举在下面了~
a) GET的数据在 URL 中对所有人都是可见的。POST的数据不会显示在 URL 中。
b) GET对数据长度有限制，当发送数据时，GET 方法向 URL 添加数据,URL 的长度是受限制的（URL 的最大长度是 2048 个字符）。POST无限制。
c) GET可收藏为书签，POST不可收藏为书签。
d) GET后退按钮/刷新无影响，POST数据会被重新提交（浏览器应该告知用户数据会被重新提交）。
e) GET编码类型application/x-www-form-url，POST编码类型encodedapplication/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
f) GET历史参数会保留在浏览器历史中。POST参数不会保存在浏览器历史中。
g) GET只允许 ASCII 字符。POST没有限制。也允许二进制数据。
h) 与POST相比GET的安全性较差，因为所发送的数据是URL的一部分。在发送密码或其他敏感信息时绝不要使用GET。POST比GET更安全，因为参数不会被保存在浏览器历史或web服务器日志中。
以上这些点都是我们常见的，还有一些我们不常见的，比如GET请求只会有一次TCP连接，而POST请求会有两次TCP连接。在这背后也有许多的设计和考虑~
所以在我们不要认为GET和POST请求有什么区别是一个很简单的问题哦~很多简单的问题背后都有着很复杂的背景。

form表单提交数据:
Method: GET POST等;
action: URL(数据提交到的地址);
name : 规定表单的名称;
target : 规定在何处打开 action URL;
autocomplete : 规定是否启用表单的自动完成功能;
accept-charset : 规定表单提交时使用的字符编码;
enctype : 规定在发送表单数据之前如何对其进行编码（适用于 method="post" 的情况）
*************
*           *
*    ajax  	*
*           *
*************
Ajax类比定外卖步骤：
定外卖: 	                         	ajax:
【1】需要一个手机或电脑                 【1】需要一个浏览器
【2】美团外卖/饿了么/百度外卖         	【2】xhr对象
【3】打开app 商家 商品               	【3】xhr.open(method, url, true)初始化ajax信息,method是方法,url是地址,true是异步方式
【4】下单                           	【4】xhr.send();
【5】监听外卖信息                    	【5】onreadystatechange == 4
【6】开门 验货 处理                  	【6】status == 200(成功)403(服务器不给)503(服务器出现问题)
<html>
	<ul id="ul">
	</ul>
	<input type="text" name="username" id="username">
	<input type="text" name="age" id="age">
	<input type="submit" id="sub">
	<script type="text/javascript">
		function ajaxFunc(options){
			function formsParams(data){
		        var arr = [];
		        for(var prop in data){
		            arr.push(prop + "=" + data[prop]);
		        }
		        return arr.join("&");
		    }
			var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),	//创建ajax对象,兼容IE
				params = formsParams(options.data),
				method = options.type.toUpperCase();

			if(method == 'GET'){ 								//GET方法
				var timer = (new Date).getTime();
				xhr.open(method, options.url +'?'+ params + '&timer=' + timer, options.async); 	//添加timer时间戳保证url的唯一性,只要url唯一就可以防止取到缓存中的数据
				xhr.send(null);
			}else if(method == 'POST'){ 						//POST方法
				xhr.open(method, options.url, options.async);
				xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") 		//post方法需要设置请求头,信息编码格式
				xhr.send(params);	
			}		
			xhr.onreadystatechange = function(){
				if(xhr.readyState == 4 && xhr.status == 200){ 	//代表请求的数据相应回来了,并且浏览器已经解析完请求,可以用了
					options.success(xhr.responseText);
				}
			}		
		}
		function showList(data){
			var value = JSON.parse(data);
			var str = '';
			value.forEach(function(ele, index){
				str += '<li>' + ele.title + '-' ele.date + '</li>'
			})
			ul.innerHTML = str;
		}
		sub.onclick = function(e){
			var event = e || window.event;
			event.preventDefault(); 							//取消默认事件
			var userNameValue = username.value,
				ageValue = age.value;
			ajaxFunc({
		    	url : "post.php",  	// url---->地址
		    	type : "POST",   	// type ---> 请求方式
		    	async : true,   	// async----> 同步：false，异步：true 
		    	data : {        	//传入信息
		        	name : userNameValue,
		        	age : ageValue
		    	},
		    	success : function(data){   //返回接受信息
		        	showList(data);
		    	}
			})
		}
	</script>
</html>
*****************
*        		*
*  JSON与JSONP  *
*        		*
*****************
JSON是一种传输数据的格式（以对象为样板，本质上就是对象，但用途有区别，对象就是本地用的，json是用来传输的）
JSON形式的对象里属性名必须要加双引号,以表示他是JSON格式的对象
JSON对象的两个方法：
JSON.parse();  		string 	--> json   	//字符串转化为JSON格式的数据
JSON.stringify(); 	json 	--> string  //JSON格式的数据转化为字符串
JSON：
<script type="text/javascript">
	var obj = {
		"name" : "li",
		"age" : 25,
		"male" : true
	}
	var str = JSON.stringify(obj);
	console.log(str) 						//打印 {"name":"li","age":25}
	console.log(typeof(str)); 				//打印 string
	console.log(JSON.parse(str)); 			//打印 {name: "li", age: 25}
	console.log(typeof(JSON.parse(str))) 	//打印 object
</script>
********************************************************************************

通过defer、Async与动态创建script方式产生的script标签不会阻塞页面中其他script的加载:
<script type="text/javascript">
	var oScript = document.createElement('script');
	oScript.src = './index.js'; 			//在index.js里定义了a
	document.body.appendChild(oScript);
	console.log(a);							//报错,a未定义,因为是异步加载所以打印a时,a还没有被加载进来
</script>
JSONP:
<script type="text/javascript">  
    function jsonpCallback(result) {  
        alert(result);  
        for(var i in result) {  
            alert(i + ":" + result[i]);		//循环输出a:1,b:2,etc.  
        }  
    }  
    var JSONP = document.createElement("script");  
    JSONP.type = "text/javascript";  
    JSONP.src = "http://crossdomain.com/services.php?callback=jsonpCallback";  
    document.getElementsByTagName("head")[0].appendChild(JSONP);  
</script> 

<script type="text/javascript">
	var oScript = document.creatElement('script');
	oScript.type = "text/javascript"; 
	oScript.src = './index.txt?callback=aaa'; 	//后台文件,内部写入aaa({'name':'dg'})(这是一个函数执行的形式);
	function aaa(data){
		console.log(data); 						//打印 {'name':'dg'}
	}
</script>
JSONP模拟百度联想词:
<html>
	<div class="wrapper">
		<input type="text" name="">
		<ul></ul>
	</div>
	<script type="text/javascript">
		var oInput = document.getElementsByTagName('input')[0],
			oUl = document.getElementsByTagName('ul')[0];
		oInput.oninput = function(){
			var value = this.value,
				oScript = document.createElement('script');
			oScript.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+ value + '&cb=doJson';
			document.body.appendChild(oScript);
			document.body.removeChild(oScript);
		}
		function doJson(data){
			var dataArr = data.s,
				str = '';
			if(dataArr.length > 0){
				dataArr.forEach(function(ele, index){
					str += '<li><a href=https://www.baidu.com/s?wd='+ ele + '>' + ele + '</li>';
				});
				oUl.innerHTML = str;
				oUl.style.display = 'block';
			}else{
				oUl.style.display = 'none';
			}
		}
	</script>
</html>
*****************
*               *
*    防抖&节流 	*
*               *
*****************
一、防抖
<!-- 在最后1次输入完后要等待一段时间提交 -->
使用变量私有化的方法封装防抖函数:
<body>
	<button id="btn">btn</button>
	<input type="text" id="search">
<script type="text/javascript">
	//封装 使用了闭包,返回的debounced是一个闭包
	function debounce(handler, time, flag){	//flag为true是先执行后等待,为false是先等待后执行
		var timer = null;					//初始化定时器
		console.log(this); 					//例子中this指向window,因为search.oninput = debounce()时,debounce函数是自执行的
		var debounced =  function(){
			var _this = this,	  			//this指向search,因为debounce函数自执行后返回了function debounced(){...},
											//然后使用search.oninput = function debounced(){...};即search对象上的oninput方法 = debounced方法
											//所以search对象在执行oninput方法时,相当于search对象调用了debounced方法,即this指向search对像
											//使用_this存储当前的this
				arg = arguments;			//对于例子来说,arguments就是在search对象调用oninput函数时传入的参数,
											//即search.oninput触发时传入的参数,很明显这里是空,但其实在执行触发search.oninput时,系统默认传入了事件对象[Event]为参数
			clearTimeout(timer);
			if(flag){ 									//flag为true代表先执行后等待
				if(!timer)handler.apply(_this, arg);	//如果timer = null,就立即执行func.apply(_this,arg); -->因为这里是先执行后等待
				timer = setTimeout(function(){ 			//无论timer是否为null都执行这一步,
														//令time = setTimeout(function(){timer = null;}, time)),所以此时time有值,不为null
														//过完time时间后令timer = null
					timer = null;
				}, time)
			}else{
				timer = setTimeout(function(){
					console.log(arg);			//[Event], 执行oninput时,系统默认传入事件对象参数
					handler.apply(_this, arg); 	//使用bind就不会立即执行,这里需要立即执行所以使用了apply(call也可以)
				}, time)
			}
		}
		debounced.cancel = function(){ 			//给debounced添加取消方法
			clearTimeout(timer);
			timer = null;
		}
		return debounced;
	}
	//使用:
	var setUseAction = debounce(function(e){ 	//相当于search.oninput = setUseAction = debounced();
		console.log(this.value);		
	}, 3000, false);
	search.oninput = setUseAction; 				//setUseAction是一个函数,每次触发search.oninput事件时都会触发这个函数,并把事件对象作为参数传入这个函数
	btn.onclick = setUseAction.cancel;
</script>
</body>
二、节流：
<!-- 1秒钟内不管点击多少次都算1次 -->
<body>
<div id="box" style="width: 300px;height: 200px;background-color: #ccc;color: #fff;text-align: center;line-height: 200px;font-size: 30px;">0</div>
<script type="text/javascript">
	//方法1(时间戳方式)
	function throttle1(handler, waitTime){
		var lastTime = 0;
		return function(){
			var now = +new Date(); 				//相当于var now = new Date(); now.getTime()
			if(now - lastTime > waitTime){
				//handler()						//这里不直接调用handler而是使用handler.apply(this, arguments);是因为如果直接使用handler,
												//则在例子中console.log(this)会打印window;而这样写会改变this指向,打印box
												//原因:box.onmousemove = throttle();其实是box.onmousemove = return function(){...}
												//也就是box上的onmousemove函数 = function(){...},所以在触发box.onmousemove函数时,
												//其实是box对象在调用function(){...},所以this指向box,写在例子中的console.log(this);其实就是写在handler里的代码,
												//所以,其实consoel.log(this)里的this指向就是handler里this的指向
												//如果直接写handler(),则是window调用的handler,所以this会指向window
				handler.apply(this, arguments); //不需要接收this是因为外面没有计时器函数,this直接指的就是function自己
				lastTime = now;
			}
		}
	}
	//方法2(使用计时器)
	function throttle2(handler, waitTime){
		var timer = null;
		return function(){
			var _this = this;	//这里需要接收this和arguments是因为在接下来的内层循环中会使用到此时的this和arguments,而内层循环又是定时器函数,是由window调用的
								//所以会改变内层函数的this指向,所以需要这里储存this和arguments来方便内层函数调用
			var argu = arguments;
			if(!timer){
				timer = setTimeout(function(){
					handler.apply(_this,argu); 	//因为内层是计时器,所以要接收一下this和arguments
					timer = null;
				}, waitTime)
			}
		}
	}
	//使用:
	box.onmousemove = throttle1(function(){
		console.log(this);
		box.innerHTML++;
	}, 1000);
</script>
</body>
*************
*           *
*  cookie	*
*           *
*************
保存用户信息的方法:
1.标记用户身份http请求首部:
【1】form: 		e-mail
【2】user-agen:	浏览器
【3】referer: 		跳转链接来自哪个网站
2.根据客户端ip地址进行识别
3.用户登录
4.胖URL
5.cookie

cookie有个数和大小的限制,一般在4k左右
cookie页面用来保存信息,比如自动登录,记住用户名
cookie特性: 同一个网站中所有页面共享一套cookie,数量大小有限,有过期时间
js中使用cookie: document.cookie,
cookie存在客户端中,客户可以更改cookie,cookie只有在服务器情况下才能正常运行
cookie的使用 :
设置cookie遵循的格式 : '名字=值'; 后写的不会覆盖先写的,但同一个属性的cookie后面的会覆盖前面的;
<script type="text/javascript">
	document.cookie = 'user=blue'; 	//cookie要一个一个写,同一个cookie'里分号后面的语句都被认为是对这条cookie的描述
	document.cookie = 'pass=123';
	document.cookie = 'pass=456';
	alert(document.cookie); 		//弹出'user=blue';'pass=456'第一条不会被覆盖
</script>
使用new Date()获取当前时间:
<script type="text/javascript">
	var oDate = new Date();
	alert(oDate.getFullYear()+'-'+(oDate.getMonth()+1)+'-'+oDate.getDate());
</script>
过期时间设置:
【1】使用expires设置;expires=时间,如果没有指定时间就默认为浏览器关闭时为过期,指定过期时间:expirees=
<script type="text/javascript">
	var oDate = new Date();
	oDate.setDate(oDate.getDate()+30);
	document.cookie = 'user=blue;expires=' + oDate;
	alert(document.cookie); 	
</script>
【2】使用max-age设置
<script type="text/javascript">
	document.cookie = 'user=aimee;max-age=1000';
	document.cookie = 'age=18'
</script>
cookie设置:
<script type="text/javascript">
	document.cookie = 'age=18;max-age=1000';
	document.cookie = 'name=aimee;expires=' + oDate;
	document.cookie = 'age=19;max-age=1000'; 						//会覆盖'age=18;max-age=1000',同一个属性的cookie后面的会覆盖前面的
	document.cookie = 'school=duyi;expires=' + oDate + ';path=/'; 	//设置路径
	document.cookie = 'age=18;max-age=-1';  						//使用保质期过期删除
</script>
封装cookie:
<script type="text/javascript">
	var manageCookie = {
		setCookie: function(name, value, time){
			document.cookie = name + '=' + value + ';max-age=' + time;
			return this; 										//方便链式操作
		},
		getCookie: function(name, callback){
			var allCookieArr = document.cookie.split('; ');
			for(var i = 0; i < allCookieArr.length; i++){
				var itemCookieArr = allCookieArr[i].split('=');
				if(itemCookieArr[0] == name){
					callback(itemCookieArr[1]);
					return this;
				}
			}
			callback(undefined);
			return this;
		},
		removeCookie: function(name){
			return this.setCookie(name, '', -1);
		}
	}
	manageCookie
		.setCookie('color', 'orange', 1000)
		.setCookie('teger', 'anime', 2000)
		.removeCookie('teger')
		.getCookie('color', function(data){
			console.log(data);
		});
</script>
**************
*            *
*   iframe   *
*            *
**************
iframe受跨域限制如何解决:
【1】document.domain: 解决跨域限制最好的办法(两个域名必须属于同一个基础域名,而且所用的协议,端口都要一致,否则无法跨域)
【2】window.location.hash: 解决子页面访问父页面数据问题(window.location.href)
【3】window.name: 解决父页面访问子页面的数据问题
iframe就是一个标签dom元素
可以一个网页里嵌入另一个网页:导航栏tab切换页(古老的做法)、在线编辑器、广告植入
历史记录管理器,解决ajax化网站相应浏览器前进后退按钮的方案,跨域通信等
contentWindow: 只有在同源的情况下父页面才能拿到子页面的contenWindow

一、如何判断iframe加载完成:
非ie下使用onload: iframe(dom元素).onload = function
ie下使用onreadystatechange或者设定定时器
<body>
	<iframe src="./son.html" frameborder="1" name="son"></iframe>
	<script type="text/javascript">
		var oIframe = document.getElementsByTagName('iframe')[0];
		iframe.onreadystatechange = function(){
			if(iframe.readyState == 'complete'||iframe.readyState == 'load'){
				alert('Local iframe is loaded');
			}
		}
	</script>
</body>
二、iframe父子页面间的数据传递:
【1】iframe父页面向子页面取值:
<body>
	<iframe src="./son.html" frameborder="1" name="son"></iframe>
	<script type="text/javascript">
		var oIframe = document.getElementsByTagName('iframe')[0];
		oIframe.onload = function(){ 	//使用src引入iframe标签是异步加载,所以要等iframe加载完后再打印
			console.log(oIframe.contentWindow.age);
		}
	</script>
</body>
【2】iframe子页面向父页面取值:
<script type="text/javascript">
	//子页面下的script标签
	console.log(window.parent.age); 		//父页面里有age变量
</script>
【3】父页面给子页面传值(使用iframe跨域读取哈希值):
<!-- 	父域 -->
<body>
	<iframe src="./son.html" frameborder="1" name="son"></iframe>
	<script type="text/javascript">
		var oIframe = document.getElementsByTagName('iframe')[0],
			age = 18,
			oSrc = oIframe.src;
		document.onclick = function(){ 		//每点击一次父域,子域的src的哈希值会加1
			oIframe.src = oSrc + "#" + age;
			age++; 
		}
	</script>
</body>
<!-- 子域 -->
<body>
	<script type="text/javascript">
		var lastHash = window.location.hash; 		//用lastHash获取当前iframe页的哈希值(这一句只是在程序第一次进入时有用,以后就没用了)
		setInterval(function(){ 					//每隔16ms秒执行一次
			if(lastHash != window.location.hash){ 	//如果lastHash与当前window.location.hash不等(每隔16ms秒监听一次location.hash是否改变)
				lastHash = window.location.hash; 	//重新赋值lastHash
			}
		}, 16);
	</script>
</body>
【4】子页面给父页面传值(window[prop]):
<!-- 子域 -->
<body>
	<script type="text/javascript">
		window.name = age;
	</script>
</body>
<!-- 父域 -->
<body>
	<script type="text/javascript">
		var flag = true;
		var oIframe = document.getElementsByTagName('iframe')[0];
		oIframe.onlaod = function(){ 						//iframe加载完之后就执行下面代码
			if(flag){
				oIframe.src = './xl.html';					//iframe重新加载同源下的资源
				flag = false; 								//通过flag开关让iframe不会无限加载'./xl.html'页面
			}else{
				console.log(oIframe.contentWindow.name) 	//打印iframe下的window.name
			}		
		}
	</script>
</body>
*********************
*                   *
*       CSS3   	    *
*                   *
*********************
*                   *
* 	css3新增选择器  	*
*                   *
*********************
div[data = 'demo'];
div[data ^= 'd' ];以开头
div[data $= 'o'];以o结尾
div[data *= 'd'];存在d
div[data ~= 'demo'];存在以demo为单词
初级伪类选择器:
:root跟标签选择器
div:not([data~='demo'])否定选择器
div:empty空标签选择器
div:target目标选择器,点击哪个哪个就是目标
<html>
	<head>
		<style>
			div:target{
				background: greenyellow;
			}
		</style>
	</head>
	<body>
		<a href="#demo1">demo1</a>		<!-- 点击这里<div id='demo1'>demo1</div>背景会变为绿黄色 -->
		<a href="#demo2">demo2</a>		<!-- 点击这里<div id='demo2'>demo2</div>背景会变为绿黄色 -->
		<a href="#demo3">demo3</a>		<!-- 点击这里<div id='demo3'>demo3</div>背景会变为绿黄色 -->
		<div class="demo" id="demo1">demo1</div>
		<div class="demo" id="demo2">demo2</div>
		<div class="demo" id="demo3">demo3</div>		
	</body>
</html>
div:first-child找第一个孩子,div:last-child找最后一个孩子,
div:nth-child(2n+1)n从0开始,div:nth-last-child(2n+1)从后往前数
div:first-of-type找类型下的第一个孩子,div:last-of-type找类型下的最后一个孩子
div:nth-of-type(2n+1)类型下的子元素,n从0开始,div:nth-last-of-type(2n+1)类型下的子元素,从后往前数
div:only-child唯一子元素的div,即该子元素不能有兄弟元素,它的父元素只有他一个直接子元素
div:only-of-type选择第某类特定的子元素,在兄弟节点中是此类元素唯一
表单元素:
input:enable(可使用的)/ input:disable(不可使用的); input:read-only(只读)/ input:read-write(非只读); input:checked(被选中的)
伪元素选择器:
div::selection光标选中元素
p::first-line选择p标签内文字第一行/ p::first-letter选择p标签内文字第一个字符
p::before标签前面/ p::after标签后面
.demo > .son   demo类的名为son类的直接子元素/ .demo .son   demo类的名为son类的子元素(可以不是直接子元素)
.demo + p 紧挨着demo类的p标签/ .demo ~ p  demo类后面所有的p标签
*********************
*                   *
* 	 css3新增样式  	*
*                   *
*********************
边框与背景：
background-origin: content-box背景图片的起始位置在内容区/ padding-box/ border-box
background-position: left/ center/ right在background-origin设置好的区域内显示图片的方式 
background-clip: 背景图片的裁剪方式content-box/ padding-box/ border-box
background-size: contain背景图片的大小,按比例扩大/ 缩小原图片,以最短边为准,不够部分空白填充; cover以最长边为准,多出部分截断处理,具体像素值,背景图片按照给定宽高全部展示
background-attachment: scroll/ auto/ fixed设置背景图片是否为固定
文本和颜色:
text-shdow: 文字阴影(水平偏移量/ 竖直偏移量/ 模糊半径/ 颜色)
box-shdow: 盒子阴影(水平偏移量/ 竖直偏移量/ 模糊半径/ 扩展半径/ 颜色/ 内外阴影)
word-wrap: 文字换行,break-word强制换行/ normal单词换行不是单词不换行
word-break: break-all/ keep-all
white-space: 对换行符、空白符的操作,pre保留所有空格包括换行符; nowrap不换行,文字将展示在一行内,并且不识别换行符和多空格符;
pre-line保留换行符,合并空格,换行由单词长度决定,单词展现完全;pre-wrap保留换行和空格正常换行
文字超出打点展示:
<style>
	{	overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
	}
</style>
自定义字体:
不同浏览器对同一种字体兼容性不一样,所以src要写多个值
<style>
	@font-face
	{	font-family: myFirstFont;
		src: url('Sansation_Light.ttf'),
		     url('Sansation_Light.eot'); /* IE9 */
	}
</style>
渐变色背景：
<style>
    /*线性渐变*/
    linear-gradient(to top/ to left top/ 20deg, red 0%, yellow 20%, green 100%)
    /*径向渐变*/
    radial-gradient(circle at center/ circle at 10px 10px/ circle at 0% 100%, red 50% yellow 100%)
</style>
*********************
*                   *
* 	 css3新增布局  	*
*                   *
*********************
列布局：
	columns: 多列布局,列宽度/ 多少列,列与列之间会隔一个制表符宽度(根元素html的1个字体像素高度16px)
	column-gap: 列与列之间宽度
	column-rule: 列与列分割线
	column-span: 跨列展示
盒模型: 
	box-sizing: border-box(IE6混杂模式盒模型)/ content-box(w3c标准盒模型)
	w3c标准盒模型中宽高就是真实宽高,不包括border和padding
	IE6混杂模式盒模型中包括border+padding真实宽高要小于等于自定义的宽高
	overflow-x或overflow-y只要设置一个为hidden/ scroll另一个若超出就有一个默认值为scroll,出现滚动条,visable正常展示
弹性盒模型:
父元素的设置：
	http://www.runoob.com/w3cnote/flex-grammar.html
	弹性盒子display: flex(父级属性),flex为复合属性,且必须配合父元素display: flex使用
	设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。
    主轴和侧轴: 
    主轴的方向：flex-direction: 例：
<style type="text/css">
    div{
		display:flex;
		flex-direction: row-reverse;
	}	
</style>
    子元素沿主轴的排列方式：justify-content: flex-start | flex-end | center | space-between | space-around
    子元素沿交叉轴的排列方式：align-content: flex-start | flex-end | center | space-between | space-around | stretch; (align-content属性只适用于多行的flex容器)
 	子元素沿交叉轴的对齐方式：align-items : flex-start | flex-end | center | baseline(基线,文字的第一行的位置称为基线) | stretch
 	设置或检索弹性盒模型对象的子元素排列方式: flex-flow: flex-direction(沿主轴方向),flex-wrap(是否换行),
子元素的设置:
	以下6个属性设置在项目(子元素上): 
	order　　	项目的排列顺序。数值越小，排列越靠前，默认为0。
	flex-grow　	项目的放大比例，默认为0，即如果存在剩余空间，也不放大。
	flex-shrink 项目的缩小比例，默认为1，即如果空间不足，该项目将缩小。
	flex-basis　	在分配多余空间之前，项目占据的主轴空间（main size）。浏览器根据这个属性，计算主轴是否有多余空间。它的默认值为auto，即项目的本来大小。
	flex　　 	是flex-grow, flex-shrink 和 flex-basis的简写，默认值为0 1 auto。后两个属性可选。
	align-self　	允许单个项目有与其他项目不一样的对齐方式，可覆盖align-items属性。默认值为auto，表示继承父元素的align-items属性，如果没有父元素，则等同于stretch。

形态转换：
transform: rotateX(30deg)旋转,带着坐标轴旋转/ rotate3d(30deg); scale(0.5,2)缩小/ 放大,不管扩大还是缩小中心点位置不变,传两个值代表宽/ 高;scaleX/scaleY/scaleZ/scale3d
skew(30deg)倾斜30度;
translate平移
transform-origin: 设置变换原点
transition展示变换过度的效果: 需要过度的css属性, 过渡动画执行时间, 过渡方式
****注意写3d动画时不要在3d变换的级写overflow: hidden,可以给他的父级或子级加
*********************
*                   *
* 	 css3 3d变换  	*
*                   *
*********************
<html>
	<head>
		<style>
			.wrapper{
				width: 500px;
				height: 500px;
				border: 1px solid #000;
				transform-style: preserve-3d;
				margin: 100px auto;
				display: flex; 				/*弹性盒模型*/
				justify-content: center; 	/*子元素延主轴居中*/
				align-items: center; 		/*子元素延侧轴居中*/
				perspective: 600px; 		/*景深600px*/
			}
			.demo{
				width: 200px;
				height: 200px;
				background: red;
				backface-visibility: hidden; /*子元素不看背面*/
			}
		</style>
	</head>
	<body>
		<div class="wrapper">
			<div class="demo"></div>
		</div>
	</body>
</html>

上下左右居中:
自身居中:{left: 50%; top :50%; transform: translateX(-50%) translateY(-50%)}
子元素居中:{ display: flex; justify-content: center; align-items: center;}

animation与transition区别:
transition趋向于起始和终止的位置,animation趋向于一个过程

响应式布局:
meta name='viewport'(视窗) content='width=device-width'(可视区宽度等于设备宽度),
initial-scale=1.0(缩放比例) user-scalable(是否允许用户缩放) minimum-scale/ maximum-scale最小最大缩放比
响应式设计的三个条件:
【1】设置的宽高不能是固定的
【2】页面中用到的图片不固定宽高
【3】不同的显示风格,需要在MediaQuery(媒体查询)上设置不同的样式(在不同终端上正常展示页面,让用户体验不变)
响应式设计中的术语:
【1】流体网格:可伸缩的网格(大小宽高 都可伸缩(可用flex))
【2】弹性图片:图片自适应(可把图片放在背景图片的位置 让div自适应)
【3】媒体查询:让页面在不同的终端上面展示效果相同(用户体验相同)
【4】屏幕分辨率:
【5】主要断点:设备宽度的临界点
【6】单位:em相对于父级的大小;rem相对于根节点(html)的大小
易混淆的宽度:
device-width/ height是设备的宽度(如电脑手机的宽度 不是浏览器的宽度)
width/height使用documentElement.clientWidth/ Height即viewport的值。渲染宽度/高度

像素:
物理像素:最大分辨率——电脑厂商定义
实际分辨率:实际自己设置的电脑分辨率——自定义的
设备独立像素:——操作系统定义的
css像素:感官像素——浏览器定义的(css像素是一个变化值)——(参考像素:根据设备独立像素做了一层调节,css像素根据参考像素进行调节)
设备像素比=实际分辨率/css像素

媒体查询:
【1】link标签
<link rel="stylesheet" type="text/css" media="Screen and (max-width:800px)" href="index.css">
在彩色电脑屏幕上并且最大可视区宽度为800px的设备上使用index.css上的样式
【2】css3新增的@media
<style type="text/css">
	@media screen and (max-width: 700px) and(min-width: 500px){
		div{
			background-color: green;
		}
	}
</style>
【3】@import(ie6、ie7不兼容)
<style type="text/css">
	@import url('index.css') screen and (max-width: 500px);	
</style>
****************
*              *
*     Canvas   *
*              *
****************
坐标轴变换
【1】translate(dx, dy) 			重新映射画布上的(0,0)位置
【2】scale(sx, sy)     			缩放当前绘图
【3】rotate(Math.PI)   			旋转当前的绘图(先旋转后画)
【4】save()/ restore() 			保存当前图像状态的一份拷贝,从栈中弹出存储的图形状态并恢复
【5】setTransform(a,b,c,d,e,f)  	先重置(清空之前的变换效果)再变换,参数:水平缩放、水平倾斜、垂直倾斜、垂直缩放、水平移动、垂直移动
【6】transform(a,b,c,d,e,f)     	在之前的基础上(保留之前的变换效果)变换
填充图案
一定要等图片加载完之后才能放入canvas中,否则会显示一块黑
【1】createPattern(image, 'repeat|repeat-x|repeat-y|no-repeat')
【2】img元素(Image对象),canvas元素,video元素(有图形的)
渐变
【1】createLinearGradient(x1, y1, x2, y2);线性渐变,必须在填充渐变的区域里定义渐变,否则没有效果
【2】createRadialGradient(x1, y1, r1, x2, y2, r2);径向渐变
【3】bg.addColorStop(p,color);
<body>
	<canvas id="can" width="500px" height="500px"></canvas>
	<script type="text/javascript">
		var can = document.getElementById('can');
		var ctx = can.getContext('2d');	
		//线性渐变
		// var bg = ctx.createLinearGradient(0,0,100,100);
		//径向渐变
		var bg = ctx.createRadialGradient(250,250,100,250,250,200);
		bg.addColorStop(0, 'red');
		bg.addColorStop(0.5, 'orange');
		bg.addColorStop(1, 'green');
		ctx.fillStyle = bg;
		ctx.fillRect(0,0,300,300);
	</script>
</body>
在canvas内部绘制图片:
<body>
	<canvas id="can" width="500px" height="500px"></canvas>
	<script type="text/javascript">
		var can = document.getElementById('can'),
			ctx = can.getContext('2d'),
			oImg = new Image();
		oImg.src = './a.jpg';
		oImg.onload = function(){
			var fill = ctx.createPattern(oImg, 'no-repeat');
			ctx.fillStyle = fill;
			ctx.fillRect(0, 0, 100, 100);
		}
	</script>
</body>
在canvas内部引入图片:
以下代码必须在localhost环境下运行
<body>
	<img id="myImg" src="./a.jpg">
	<canvas id="can" width="500px" height="500px"></canvas>
	<script type="text/javascript">
		var myImg = document.getElementById('myImg');
		// myImg.setAttribute('crossorigin', 'anonymous');	解决图片跨域问题
		myImg.onload = function(){
			var can = document.getElementById('can'),
				ctx = can.getContext('2d'),
				w = this.width,
				h = this.height;
	      	ctx.drawImage(this, 0, 0, w, h);
	      	/*传3个参数: (myImg, x, y),起始坐标点; 传5个参数: (myImg, x, y, w, h),起始点坐标及图片所存区域的宽高*/
	      	/*传9个参数: (myImg, x1, y1, dx1, dy1, x2, y2, w2, h2)前4个为所绘制目标元素的起始点和宽高,后4个为canvas绘制的起始点和大小*/
	      	var data = can.toDataURL(); 					/*把canvas图像抽取成1个data64位编码的图片*/
	      	console.log(data);
		};
	</script>
</body>

获取像canvas 像素信息
【1】ctx.getImageData(x, y, dx, dy) 	遵循同源策略
【2】ctx.createImageData(w, h) 		创建新的空白ImageData对象
【3】ctx.putImageData(imgData,x,y) 	将图像数据放回画布上

命中检测
【1】ctx.isPointInPath(x,y); 	检测是否在区域内
【2】ctx.isPointInStroke(x,y);	检测是否在线上

如何解决canvas高分屏模糊问题
在分辨率比较高的屏幕内,因为canvas绘制的是位图,所以会导致模糊,解决方法是根据屏幕分辨率修改canvas样式代码中的宽和高与canvas的width和height属性的比例

requestAnimationFrame:页面刷新前执行一次,用法和 setTimeout类似
requestAnimFrame兼容性写法:
<script type="text/javascript">
	window.requestAnimFrame = (function(){
		return window.requestAnimationFrame ||
			  	window.webkitRequsetAnimationFrame ||
			  	window.mozRequestAnimationFrame ||
			  	function(callback){
			  		window.setTimeout(callback, 1000/60);
			  	}
	})()
</script>
cancelAnimFrame兼容性写法:
<script type="text/javascript">
	window.cancelAnimFrame = (function(){
		return window.cancelAnimationFrame ||
				window.webkitCancelAnimationFrame ||
				window.mozCancelAnimationFrame ||
				function(id){
					window.clearTimeout(id);
				}
	})()
</script>
****************
*              *
*     HTML5    *
*              *
****************
*              *
*   客户端存储  *
*              *
****************
一、客户端存储:storage、cookie
	storage: localstorage、seesionstorage
如何存储数据:
	localStorage.name = 'aimee'
	localStorage.info = JSON.stringify({name:'aimee,company: 'duyi'})
如何取出数据
	localStrorage.name
	JSON.parse(localStorage.info)
存储的有效期
	localStorage  -> 永久的,除非手动删除
	sessionStorage-> 临时,窗口关闭就没有了
作用域
	localStorage  ->文档源限制
	sessionStorage->文档源限制+窗口
API:
【1】setItem(name, val) 	设置属性值
【2】getItem(name) 		获得属性值
【3】removeItem(name) 	移除属性
【4】clear() 			清除属性

二、cookie 
存储信息到用户的设备上，数据量较小

navigator.cookieEnabled
检测是否启用了cookie

【1】设置cookie值：
document.cookie = 'name=aimee'（每次只能设置一个值，因为浏览器会认为后面的键值对是这个cookie的属性(比如'max-age')）
【2】获得cookie值：
document.cookie

不建议出现分号，逗号，空格的奇怪的符号
<script type="text/javascript">
	function getCookie(name) {
		var name = name + "=";
		var cookieArr = document.cookie.split(';');
		for(var i = 0; i < cookieArr.length; i++) {
			var c = cookieArr[i];
			while (c.charAt(0) == ' ') c = c.substring(1); 	/*去掉cookie首部的空格*/
			if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
		}
		return "";
	}
</script>
【1】设置cookie存储期限
	document.cookie = 'name=scott;max-age=1000'; 	单位: 秒
【2】expires 当前时间加上保存时间
<script type="text/javascript">
	var timestamp = (new Date()).getTime() + 10000;
	var expires = new Date(timestamp).toGMTString();
	document.cookie = 'name=scott;expires=' + expires;
</script>
【3】domain
【4】path

删除cookie:
<script type="text/javascript">
	//方法1: max-age=0; 
	document.cookie = 'name=scott;max-age=0';
	//方法2: expires 设置为之前的时间
	document.cookie = 'name=scott;expires=' ;
</script>

三、history
【1】history.back()
【2】history.forward()
【3】history.go(n)

为什么要管理历史记录 ？ history api的作用？
通过修改hash和hashchange事件来实现历史纪录管理

【1】history.pushState(state, title, url); 添加一条历史记录
【2】history.replaceState(state, title, url); 替换当前的历史记录

参数：
state：一个与指定网址相关的状态对象，popstate事件触发时，该对象会传入回调函数中。如果不需要这个对象，此处可以填null。 
title：新页面的标题，但是所有浏览器目前都忽略这个值，因此这里可以填null。 
url：  新的网址，必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

事件：
popstate 事件
历史记录发生改变时触发
调用history.pushState()或者history.replaceState()不会触发popstate事件
hashchange事件
当页面的hash值改变的时候触发，常用于构建单页面应用

四、Worker
【1】worker文件必须和主文件满足同源策略,例:
<script type="text/javascript">
	//创建worker
	var worker = new Worker('worker.js');
</script>
【2】worker和主线程之间的通信:
	postMessage(n)方法
	message事件
【3】结束一个worker:
	close(): 在worker作用域中调用,worker自己选择不干了
	terminate(): 在worker对象上调用(主进程的worker对象上 worker.terminate,是主线程解雇了worker)
其他特性
1、importScripts('./math1.js','./math2.js')
worker只是window的子集，只能实现部分功能，不能获取到window, document，所以这里不要引juery zepto。可以引入一些计算类的库。

2、作用域 globalWorkerScope
【1】可以加载一个JS进行大量的复杂计算而不挂起主进程，并通过postMessage，onmessage进行通信
【2】可以在worker中通过importScripts(url)加载另外的脚本文件
【3】可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()
【4】可以使用XMLHttpRequest来发送请求
【5】可以访问navigator的部分属性
<script type="text/javascript">
	//主线程
	var worker = new Worker('***.js'); 
	//仅能存放同域的处理数据用，或ajax调用数据用的js，子线程的js不能操作dom节点，因为子线程不能影响主线程对dom的操作。
	worker.postMessage(data); 	//通过postMessage方法来关联主子线程。data为任意值
	worker.terminate();     	//终止workers,一旦终止则不能再使用，需要重新new worker
	worker.message(); 			//接收信息用，放入监控中则一直有效。
	worker.error();  			//对错误的处理

	//子线程
	//子线程中的js可操作范围：
	//worker.js执行的上下文与主页面html执行时的上下文并不相同，最顶层的对象并不是window，而是WorkerGlobalScope。
	//因此window的alert方法在此js中不能用。WorkerGlobalScope的属性方法：
	self
	//我们可以使用 WorkerGlobalScope 的 self 属性来或者这个对象本身的引用
	location
	//location 属性返回当线程被创建出来的时候与之关联的 WorkerLocation 对象，它表示用于初始化这个工作线程的脚步资源的绝对 URL，
	//即使页面被多次重定向后，这个 URL 资源位置也不会改变。
	close
	//关闭当前线程，与terminate作用类似
	importScripts
	//我们可以通过importScripts()方法通过url在worker中加载库函数
	XMLHttpRequest
	//有了它，才能发出Ajax请求
	setTimeout/ setInterval以及addEventListener/ postMessage
</script>

geolocation:获取地理位置信息
<script type="text/javascript">
	function suc(pos){
		console.log(pos);
	}
	function err(error){
		console.log(error);
	}
	var options = {
		enableHightAccuracy: true,
		timeout : 1000
	}
	window.navigator.geolocation.getCurrentPosition(suc, err, options);
</script>

websocket:
<script type="text/javascript">
	var Socket = new WebSocket('ws://echo.websocket.org');
	Socket.onopen = function(){
		Socket.send('hello');
	}
	Socket.onmessage = function(e){
		console.log(e);
		console.log(e.data);
		Socket.close();
	}
	Socket.onclose = function(){
		console.log('close');
	}
</script>

前端工程化(模块化)：
<script>
	//封装函数
	function bindEvent(){
		//click, hover
	}
	//命名空间
	var obj = {
		name：'dg';
	}
	//立即执行函数防止污染全局变量
	(function(){
		var a = 0;
		function add(a,b){}
		//通过return或挂载到window来暴露接口
		window.$ = window.jQuery = jQuery;
	})(window)
	//在jQuery中给自己扩展插件
	(function($){
		$.abc = function(){

		}
	})(jQuery)
</script>

CommonJs通过export暴露自己的接口,使用require来加载一个模块
AMD通过define来定义一个模块,使用require来加载一个模块






作业：
【1】bom锚点项目(iframe)
【2】菜单栏
【3】贪食蛇游戏/斜着走
【4】扫雷游戏
【6】轮播图生成函数
【7】运算符号优先级
【9】获取渡一ajax笔记,jquery笔记
【10】完善豆瓣
【11】节流处理瀑布流
【12】css3标签页(第一集最后)
【13】14集从源码解读bootstrap,深入响应式开发框架二后面项目完善
【14】像素适配/meta
【15】自己练习各种svg小动画/手写神马
【17】三维弹球项目
[18]css3红包上下抖动，fileloader传文件页面展示
[19]echarts/canvas/svg
[20]音乐播放器底下list
[21]fiddler
【22】再谈闭包、两种深克隆对比博客
【23】数据结构
[24]算法笔记/冒泡排序
[25]哈希算法传参/封装set
[26]创建算法演示网站
BACKBONE.JS
underscore.js
React Native
[27]算法图解
[28]https://codepen.io/找项目
[29]浏览器报错汇总，6个小人解析
[30]用promise模拟父级需要继续处理子级返回值的回调地狱
[31]高斯模糊算法/RSA加密算法:http://www.cnblogs.com/vamei/p/3480994.html
[32]函数柯里化:从jesonp到手动封装ES6中promise方法













